threads / rfc / 62495

RFC v7, 22 partsMakefile: use common template for GIT-BUILD-OPTIONS

Subject: [PATCH RFC v7 01/22] Makefile: use common template for GIT-BUILD-OPTIONS

## tl;dr

31 messages between Nov 15, 2024 and Nov 18, 2024. Diffs are folded; open one to read it.

replies: 30people: 4as markdown or json

Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · lore

[PATCH RFC v7 00/22] Modernize the build system

Hi,

this patch series modernizes our build infrasturcture. It refactors various parts of it to make it possible to perform out-of-tree builds in theory.

The series then continues to wire up Meson as a proposed alternative as a modern replacement for our current build systems. It provides better integration into IDEs than our Makefiles, better usability than our Makefiles and CMake, better configuration and discoverability thereof than autoconf and CMake (which is of course my personal opinion).

The intent is to have Meson replace autoconf, which is mostly unused and broken. Eventually, the plan would to also replace CMake and our Makefiles in case we figure out that this replacement is indeed a net benefit for the project, but all of this would happen over the course of multiple releases.

Changes in v7:
  - Fix overriding TEST_OUTPUT_DIRECTORY via the environment.
  - Rework generation of version information. GIT-VERSION-GEN now knows
    to replace @PLACEHOLDERS@ in other files, and this gets used across
    Makefiles, CMake and Meson now. This makes us properly rebuild when
    the version info changes and fixes the version numbers in "git.rc".
  - The CMake build instructions are now doing a proper out-of-tree
    build.
  - We now generate and install gitweb.js, which gets built via a new
    "generate-gitweb-js.sh".
  - Some improvements to the Meson documentation.
  - Wire up DEVELOPER=1 style warnings via `--warnlevel 2` and above.
  - Wire up sanitizers properly.
  - Provide a new "regex" option to disable the platform-provided
    version of regcomp() et al.
  - Stop installing git-http-backend into bindir.
  - Install Perl scripts into libexecdir.
  - Install git-cvsserver into bindir and libexecdir.
  - Install template directory into datadir.

As before, the tip of this patch series catches up with what is happening in "seen". If you want to test it without topics in "seen" you have to revert it.

Links to previous versions:
  - Link to v1: https://lore.kernel.org/r/cover.1727881164.git.ps@pks.im
  - Link to v2: https://lore.kernel.org/r/cover.1728485139.git.ps@pks.im
  - Link to v3: https://lore.kernel.org/r/cover.1729254070.git.ps@pks.im
  - Link to v4: https://lore.kernel.org/r/cover.1729771605.git.ps@pks.im
  - Link to v5: https://lore.kernel.org/r/cover.1731335938.git.ps@pks.im
  - Link to v6: https://lore.kernel.org/r/20241112-pks-meson-v6-0-648b30996827@pks.im
Thanks!
Patrick
To: git@vger.kernel.org
Cc: Eli Schwartz <eschwartz@gentoo.org>
Cc: Eric Sunshine <sunshine@sunshineco.com>
Cc: Phillip Wood <phillip.wood123@gmail.com>
Cc: Junio C Hamano <gitster@pobox.com>
Cc: Ramsay Jones <ramsay@ramsayjones.plus.com>
Cc: Taylor Blau <me@ttaylorr.com>
Cc: David Aguilar <davvid@gmail.com>
Cc: Jeff King <peff@peff.net>
Cc: Christian Couder <christian.couder@gmail.com>
Patrick Steinhardt (22):
      Makefile: use common template for GIT-BUILD-OPTIONS
      Makefile: consistently use @PLACEHOLDER@ to substitute
      Makefile: refactor GIT-VERSION-GEN to be reusable
      Makefile: propagate Git version via generated header
      Makefile: generate "git.rc" via GIT-VERSION-GEN
      Makefile: consistently use PERL_PATH
      Makefile: extract script to massage Perl scripts
      Makefile: use "generate-perl.sh" to massage Perl library
      Makefile: extract script to massage Shell scripts
      Makefile: extract script to generate gitweb.cgi
      Makefile: extract script to generate gitweb.js
      Makefile: refactor generators to be PWD-independent
      Makefile: allow "bin-wrappers/" directory to exist
      Makefile: simplify building of templates
      Documentation: allow sourcing generated includes from separate dir
      Documentation: teach "cmd-list.perl" about out-of-tree builds
      Documentation: extract script to generate a list of mergetools
      t: better support for out-of-tree builds
      t: allow overriding build dir
      Documentation: add comparison of build systems
      Introduce support for the Meson build system
      meson: fix conflicts with in-flight topics
 .gitignore                                         |    3 +-
 Documentation/CodingGuidelines                     |    2 +-
 Documentation/Makefile                             |   28 +-
 Documentation/build-docdep.perl                    |    2 +
 Documentation/cmd-list.perl                        |   23 +-
 Documentation/config/diff.txt                      |    2 +-
 Documentation/config/merge.txt                     |    2 +-
 Documentation/generate-mergetool-list.sh           |   17 +
 Documentation/git.txt                              |   24 +-
 Documentation/meson.build                          |  317 ++++
 Documentation/technical/build-systems.txt          |  224 +++
 GIT-BUILD-OPTIONS.in                               |   47 +
 GIT-VERSION-FILE.in                                |    1 +
 GIT-VERSION-GEN                                    |   62 +-
 Makefile                                           |  232 +--
 bin-wrappers/.gitignore                            |    9 +
 bin-wrappers/meson.build                           |   28 +
 bin-wrappers/wrap-for-bin.sh                       |   37 +
 ci/test-documentation.sh                           |    2 +-
 configure.ac                                       |    2 +-
 contrib/buildsystems/CMakeLists.txt                |  287 +--
 contrib/buildsystems/git-version.in                |    1 +
 contrib/completion/meson.build                     |    8 +
 contrib/meson.build                                |    1 +
 generate-cmdlist.sh                                |   42 +-
 generate-configlist.sh                             |   20 +-
 generate-hooklist.sh                               |   15 +-
 generate-perl.sh                                   |   37 +
 generate-script.sh                                 |   34 +
 git-cvsserver.perl                                 |    2 +-
 git-instaweb.sh                                    |    8 +-
 git-request-pull.sh                                |    2 +-
 git-send-email.perl                                |    2 +-
 git-sh-i18n.sh                                     |    6 +-
 git-sh-setup.sh                                    |    6 +-
 git-svn.perl                                       |    2 +-
 git.rc => git.rc.in                                |    6 +-
 gitk-git/po/vi.po                                  |    2 +-
 gitweb/GITWEB-BUILD-OPTIONS.in                     |   24 +
 gitweb/Makefile                                    |   62 +-
 gitweb/generate-gitweb-cgi.sh                      |   47 +
 gitweb/generate-gitweb-js.sh                       |   12 +
 gitweb/gitweb.perl                                 |   44 +-
 gitweb/meson.build                                 |   79 +
 meson.build                                        | 1857 ++++++++++++++++++++
 meson_options.txt                                  |   83 +
 perl/FromCPAN/Mail/meson.build                     |    7 +
 perl/FromCPAN/meson.build                          |    9 +
 perl/Git/I18N.pm                                   |    6 +-
 perl/Git/LoadCPAN.pm                               |    6 +-
 perl/Git/LoadCPAN/Mail/meson.build                 |    7 +
 perl/Git/LoadCPAN/meson.build                      |    9 +
 perl/Git/SVN/Memoize/meson.build                   |    7 +
 perl/Git/SVN/meson.build                           |   20 +
 perl/Git/meson.build                               |   18 +
 perl/header_templates/fixed_prefix.template.pl     |    2 +-
 perl/header_templates/runtime_prefix.template.pl   |    8 +-
 perl/meson.build                                   |   12 +
 po/meson.build                                     |   27 +
 subprojects/.gitignore                             |    1 +
 subprojects/curl.wrap                              |   13 +
 subprojects/expat.wrap                             |   13 +
 subprojects/openssl.wrap                           |   15 +
 subprojects/pcre2.wrap                             |   16 +
 subprojects/zlib.wrap                              |   13 +
 t/helper/meson.build                               |   93 +
 t/lib-gettext.sh                                   |    4 +-
 t/meson.build                                      | 1107 ++++++++++++
 t/t7609-mergetool--lib.sh                          |    2 +-
 t/test-lib.sh                                      |   31 +-
 templates/Makefile                                 |   39 +-
 templates/branches--                               |    1 -
 templates/{this--description => description}       |    0
 .../applypatch-msg.sample}                         |    0
 .../commit-msg.sample}                             |    0
 .../fsmonitor-watchman.sample}                     |    0
 templates/hooks/meson.build                        |   26 +
 .../post-update.sample}                            |    0
 .../pre-applypatch.sample}                         |    0
 .../pre-commit.sample}                             |    0
 .../pre-merge-commit.sample}                       |    0
 .../pre-push.sample}                               |    0
 .../pre-rebase.sample}                             |    0
 .../pre-receive.sample}                            |    0
 .../prepare-commit-msg.sample}                     |    0
 .../push-to-checkout.sample}                       |    0
 .../sendemail-validate.sample}                     |    0
 .../{hooks--update.sample => hooks/update.sample}  |    0
 templates/{info--exclude => info/exclude}          |    0
 templates/info/meson.build                         |    7 +
 templates/meson.build                              |   15 +
 unimplemented.sh                                   |    2 +-
 version-def.h.in                                   |    8 +
 version.c                                          |    1 +
 wrap-for-bin.sh                                    |   36 -
 95 files changed, 4848 insertions(+), 486 deletions(-)
Range-diff versus v6:
 1:  7c89e63ea5 !  1:  61d8677718 Makefile: use common template for GIT-BUILD-OPTIONS
    @@ Commit message
         While at it, remove the `SUPPORTS_SIMPLE_IPC` variable that we only set
         up in CMake as it isn't used anywhere.
     
    -    Note that this change requires us to move around the setup of
    -    TEST_OUTPUT_DIRECTORY in "test-lib.sh" such that it comes after sourcing
    -    the "GIT-BUILD-OPTIONS" file. This is the only instance I could find
    -    where we rely on ordering on variables.
    +    This change requires us to adapt the setup of TEST_OUTPUT_DIRECTORY in
    +    "test-lib.sh" such that it does not get overwritten after sourcing when
    +    it has been set up via the environment. This is the only instance I
    +    could find where we rely on ordering on variables.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
    @@ contrib/buildsystems/CMakeLists.txt: if(NOT PYTHON_TESTS)
     -file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PYTHON='${NO_PYTHON}'\n")
     -file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "SUPPORTS_SIMPLE_IPC='${SUPPORTS_SIMPLE_IPC}'\n")
     +file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_CONSUME)
    -+string(REPLACE "@SHELL_PATH@" "${SHELL_PATH}" git_build_options "${git_build_options}")
    -+string(REPLACE "@TEST_SHELL_PATH@" "${TEST_SHELL_PATH}" git_build_options "${git_build_options}")
    -+string(REPLACE "@PERL_PATH@" "${PERL_PATH}" git_build_options "${git_build_options}")
    -+string(REPLACE "@DIFF@" "${DIFF}" git_build_options "${git_build_options}")
    -+string(REPLACE "@PYTHON_PATH@" "${PYTHON_PATH}" git_build_options "${git_build_options}")
    -+string(REPLACE "@TAR@" "${TAR}" git_build_options "${git_build_options}")
    ++string(REPLACE "@SHELL_PATH@" "'${SHELL_PATH}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@TEST_SHELL_PATH@" "'${TEST_SHELL_PATH}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@PERL_PATH@" "'${PERL_PATH}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@PYTHON_PATH@" "'${PYTHON_PATH}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@TAR@" "'${TAR}'" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_CURL@" "${NO_CURL}" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_ICONV@" "${NO_ICONV}" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_EXPAT@" "${NO_EXPAT}" git_build_options "${git_build_options}")
    @@ contrib/buildsystems/CMakeLists.txt: if(NOT PYTHON_TESTS)
     +string(REPLACE "@NO_PYTHON@" "${NO_PYTHON}" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_REGEX@" "" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_UNIX_SOCKETS@" "${NO_UNIX_SOCKETS}" git_build_options "${git_build_options}")
    -+string(REPLACE "@PAGER_ENV@" "${PAGER_ENV}" git_build_options "${git_build_options}")
    ++string(REPLACE "@PAGER_ENV@" "'${PAGER_ENV}'" git_build_options "${git_build_options}")
     +string(REPLACE "@SANITIZE_LEAK@" "" git_build_options "${git_build_options}")
     +string(REPLACE "@SANITIZE_ADDRESS@" "" git_build_options "${git_build_options}")
     +string(REPLACE "@X@" "${EXE_EXTENSION}" git_build_options "${git_build_options}")
    @@ contrib/buildsystems/CMakeLists.txt: if(NOT PYTHON_TESTS)
     +string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
     +string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
     +string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
    -+string(REPLACE "@RUNTIME_PREFIX@" "${RUNTIME_PREFIX}" git_build_options "${git_build_options}")
    ++string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
      if(USE_VCPKG)
     -	file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
     +	string(APPEND git_build_options "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
    @@ t/test-lib.sh: else
      GIT_BUILD_DIR="${TEST_DIRECTORY%/t}"
      if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR"
      then
    +@@ t/test-lib.sh: export LSAN_OPTIONS
    + prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS
    + export UBSAN_OPTIONS
    + 
    ++# The TEST_OUTPUT_DIRECTORY will be overwritten via GIT-BUILD-OPTIONS. So in
    ++# case the caller has manually set up this variable via the environment we must
    ++# make sure to not overwrite that value, and thus we save it into
    ++# TEST_OUTPUT_DIRECTORY_OVERRIDE here.
    ++if test -n "$TEST_OUTPUT_DIRECTORY" && test -z "$TEST_OUTPUT_DIRECTORY_OVERRIDE"
    ++then
    ++	TEST_OUTPUT_DIRECTORY_OVERRIDE=$TEST_OUTPUT_DIRECTORY
    ++fi
    ++
    + if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
    + then
    + 	echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).'
     @@ t/test-lib.sh: fi
      . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
      export PERL_PATH SHELL_PATH
 2:  ebddce640a =  2:  749b5d29e5 Makefile: consistently use @PLACEHOLDER@ to substitute
 -:  ---------- >  3:  af6b00a7b7 Makefile: refactor GIT-VERSION-GEN to be reusable
 -:  ---------- >  4:  666c1d6cf4 Makefile: propagate Git version via generated header
 -:  ---------- >  5:  0358fa0574 Makefile: generate "git.rc" via GIT-VERSION-GEN
 3:  2b8f7520bf =  6:  d99407979a Makefile: consistently use PERL_PATH
 4:  4545c43196 !  7:  47e0291d3e Makefile: extract script to massage Perl scripts
    @@ Makefile: endif
     -	    $< >$@+ && \
     -	chmod +x $@+ && \
     +$(SCRIPT_PERL_GEN): % : %.perl generate-perl.sh GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
    -+	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS $(GIT_VERSION) GIT-PERL-HEADER "$<" "$@+" && \
    ++	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@+" && \
      	mv $@+ $@
      
      PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES))
    @@ contrib/buildsystems/CMakeLists.txt: foreach(script ${git_shell_scripts})
      string(REPLACE "@PATHSEP@" ":" perl_header "${perl_header}")
      string(REPLACE "@INSTLIBDIR@" "${INSTLIBDIR}" perl_header "${perl_header}")
     +file(WRITE ${CMAKE_BINARY_DIR}/PERL-HEADER ${perl_header})
    ++
    ++add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
    ++	COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
    ++		"${CMAKE_SOURCE_DIR}"
    ++		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
    ++		"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
    ++	DEPENDS ${SH_EXE} "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
    ++		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
    ++	VERBATIM)
      
      foreach(script ${git_perl_scripts})
     -	file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.perl content NEWLINE_CONSUME)
    @@ contrib/buildsystems/CMakeLists.txt: foreach(script ${git_shell_scripts})
     +	add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${perl_gen_path}"
     +		COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-perl.sh"
     +			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
    -+			"${PROJECT_VERSION}"
    ++			"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
     +			"${CMAKE_BINARY_DIR}/PERL-HEADER"
     +			"${CMAKE_SOURCE_DIR}/${script}"
     +			"${CMAKE_BINARY_DIR}/${perl_gen_path}"
     +		DEPENDS "${CMAKE_SOURCE_DIR}/generate-perl.sh"
     +			"${CMAKE_SOURCE_DIR}/${script}"
    ++			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
    ++			"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
     +		VERBATIM)
     +	list(APPEND perl_gen ${CMAKE_BINARY_DIR}/${perl_gen_path})
      endforeach()
    @@ generate-perl.sh (new)
     +
     +if test $# -ne 5
     +then
    -+	echo "USAGE: $0 <GIT_BUILD_OPTIONS> <GIT_VERSION> <PERL_HEADER> <INPUT> <OUTPUT>" >&2
    ++	echo "USAGE: $0 <GIT_BUILD_OPTIONS> <GIT_VERSION_FILE> <PERL_HEADER> <INPUT> <OUTPUT>" >&2
     +	exit 1
     +fi
     +
     +GIT_BUILD_OPTIONS="$1"
    -+GIT_VERSION="$2"
    ++GIT_VERSION_FILE="$2"
     +PERL_HEADER="$3"
     +INPUT="$4"
     +OUTPUT="$5"
     +
     +. "$GIT_BUILD_OPTIONS"
    ++. "$GIT_VERSION_FILE"
     +
     +sed -e '1{' \
     +    -e "	s|#!.*perl|#!$PERL_PATH|" \
 5:  50948f7020 !  8:  c3f9a22ae4 Makefile: use "generate-perl.sh" to massage Perl library
    @@ Makefile: endif
      endif
      
     -perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
    -+perl/build/lib/%.pm: perl/%.pm generate-perl.sh GIT-BUILD-OPTIONS GIT-PERL-DEFINES
    ++perl/build/lib/%.pm: perl/%.pm generate-perl.sh GIT-BUILD-OPTIONS GIT-VERSION-FILE GIT-PERL-DEFINES
      	$(call mkdir_p_parent_template)
     -	$(QUIET_GEN) \
     -	sed -e 's|@LOCALEDIR@|$(perl_localedir_SQ)|g' \
     -	    -e 's|@NO_GETTEXT@|$(NO_GETTEXT_SQ)|g' \
     -	    -e 's|@NO_PERL_CPAN_FALLBACKS@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
     -	< $< > $@
    -+	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS $(GIT_VERSION) GIT-PERL-HEADER "$<" "$@"
    ++	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@"
      
      perl/build/man/man3/Git.3pm: perl/Git.pm
      	$(call mkdir_p_parent_template)
    @@ contrib/buildsystems/CMakeLists.txt: endforeach()
      
      #create perl header
      file(STRINGS ${CMAKE_SOURCE_DIR}/perl/header_templates/fixed_prefix.template.pl perl_header )
    -@@ contrib/buildsystems/CMakeLists.txt: string(REPLACE "@PATHSEP@" ":" perl_header "${perl_header}")
    - string(REPLACE "@INSTLIBDIR@" "${INSTLIBDIR}" perl_header "${perl_header}")
    - file(WRITE ${CMAKE_BINARY_DIR}/PERL-HEADER ${perl_header})
    +@@ contrib/buildsystems/CMakeLists.txt: add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
    + 		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
    + 	VERBATIM)
      
     -foreach(script ${git_perl_scripts})
     +foreach(script ${git_perl_scripts} ${perl_modules})
    @@ contrib/buildsystems/CMakeLists.txt: file(STRINGS ${CMAKE_SOURCE_DIR}/git-p4.py
      file(GLOB templates "${CMAKE_SOURCE_DIR}/templates/*")
      list(TRANSFORM templates REPLACE "${CMAKE_SOURCE_DIR}/templates/" "")
     @@ contrib/buildsystems/CMakeLists.txt: file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_
    - string(REPLACE "@SHELL_PATH@" "${SHELL_PATH}" git_build_options "${git_build_options}")
    - string(REPLACE "@TEST_SHELL_PATH@" "${TEST_SHELL_PATH}" git_build_options "${git_build_options}")
    - string(REPLACE "@PERL_PATH@" "${PERL_PATH}" git_build_options "${git_build_options}")
    -+string(REPLACE "@PERL_LOCALEDIR@" "${LOCALEDIR}" git_build_options "${git_build_options}")
    + string(REPLACE "@SHELL_PATH@" "'${SHELL_PATH}'" git_build_options "${git_build_options}")
    + string(REPLACE "@TEST_SHELL_PATH@" "'${TEST_SHELL_PATH}'" git_build_options "${git_build_options}")
    + string(REPLACE "@PERL_PATH@" "'${PERL_PATH}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@PERL_LOCALEDIR@" "'${LOCALEDIR}'" git_build_options "${git_build_options}")
     +string(REPLACE "@NO_PERL_CPAN_FALLBACKS@" "" git_build_options "${git_build_options}")
    - string(REPLACE "@DIFF@" "${DIFF}" git_build_options "${git_build_options}")
    - string(REPLACE "@PYTHON_PATH@" "${PYTHON_PATH}" git_build_options "${git_build_options}")
    - string(REPLACE "@TAR@" "${TAR}" git_build_options "${git_build_options}")
    + string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
    + string(REPLACE "@PYTHON_PATH@" "'${PYTHON_PATH}'" git_build_options "${git_build_options}")
    + string(REPLACE "@TAR@" "'${TAR}'" git_build_options "${git_build_options}")
     
      ## generate-perl.sh ##
     @@ generate-perl.sh: OUTPUT="$5"
    - . "$GIT_BUILD_OPTIONS"
    + . "$GIT_VERSION_FILE"
      
      sed -e '1{' \
     +    -e "	/^#!.*perl/!b" \
 6:  2340c63def !  9:  23f094d0ba Makefile: extract script to massage Shell scripts
    @@ Makefile: GIT-SCRIPT-DEFINES: FORCE
     +	$(QUIET_GEN)./generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \
      	mv $@+ $@
      
    - git.res: git.rc GIT-VERSION-FILE GIT-PREFIX
    + git.rc: git.rc.in GIT-VERSION-GEN GIT-VERSION-FILE
     @@ Makefile: GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
      perllibdir:
      	@echo '$(perllibdir_SQ)'
    @@ contrib/buildsystems/CMakeLists.txt: set(git_shell_scripts
     @@ contrib/buildsystems/CMakeLists.txt: string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_optio
      string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
      string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
    - string(REPLACE "@RUNTIME_PREFIX@" "${RUNTIME_PREFIX}" git_build_options "${git_build_options}")
    -+string(REPLACE "@GITWEBDIR@" "${GITWEBDIR}" git_build_options "${git_build_options}")
    + string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
    ++string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
     +string(REPLACE "@USE_GETTEXT_SCHEME@" "" git_build_options "${git_build_options}")
    -+string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" git_build_options "${git_build_options}")
    ++string(REPLACE "@LOCALEDIR@" "'${LOCALEDIR}'" git_build_options "${git_build_options}")
     +string(REPLACE "@BROKEN_PATH_FIX@" "" git_build_options "${git_build_options}")
      if(USE_VCPKG)
      	string(APPEND git_build_options "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
 7:  68c0dad962 ! 10:  457a42ef55 Makefile: extract script to generate gitweb.cgi
    @@ gitweb/GITWEB-BUILD-OPTIONS.in (new)
     +PERL_PATH=@PERL_PATH@
     +JSMIN=@JSMIN@
     +CSSMIN=@CSSMIN@
    -+GIT_VERSION=@GIT_VERSION@
     +GIT_BINDIR=@GIT_BINDIR@
     +GITWEB_CONFIG=@GITWEB_CONFIG@
     +GITWEB_CONFIG_SYSTEM=@GITWEB_CONFIG_SYSTEM@
    @@ gitweb/Makefile: GITWEB_JSLIB_FILES += static/js/javascript-detection.js
     +	     $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS.in >"$@+"
      	@cmp -s $@+ $@ && rm -f $@+ || mv -f $@+ $@
      
    -+$(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)generate-gitweb.sh
    ++$(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)generate-gitweb-cgi.sh
      $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS
    ++$(MAK_DIR_GITWEB)gitweb.cgi: GIT-VERSION-FILE
      $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)gitweb.perl
      	$(QUIET_GEN)$(RM) $@ $@+ && \
     -	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
     -		$(GITWEB_REPLACE) $< >$@+ && \
     -	chmod +x $@+ && \
    -+	$(MAK_DIR_GITWEB)generate-gitweb.sh $(MAK_DIR_GITWEB)/GITWEB-BUILD-OPTIONS $< $@+ && \
    ++	$(MAK_DIR_GITWEB)generate-gitweb-cgi.sh $(MAK_DIR_GITWEB)/GITWEB-BUILD-OPTIONS ./GIT-VERSION-FILE $< $@+ && \
      	mv $@+ $@
      
      $(MAK_DIR_GITWEB)static/gitweb.js: $(addprefix $(MAK_DIR_GITWEB),$(GITWEB_JSLIB_FILES))
     
    - ## gitweb/generate-gitweb.sh (new) ##
    + ## gitweb/generate-gitweb-cgi.sh (new) ##
     @@
     +#!/bin/sh
     +
     +set -e
     +
    -+if test $# -ne 3
    ++if test $# -ne 4
     +then
    -+	echo "USAGE: $0 <GITWEB-BUILD-OPTIONS> <INPUT> <OUTPUT>" >&2
    ++	echo "USAGE: $0 <GITWEB-BUILD-OPTIONS> <GIT-VERSION-FILE> <INPUT> <OUTPUT>" >&2
     +	exit 1
     +fi
     +
     +GITWEB_BUILD_OPTIONS="$1"
    -+INPUT="$2"
    -+OUTPUT="$3"
    ++GIT_VERSION_FILE="$2"
    ++INPUT="$3"
    ++OUTPUT="$4"
     +
     +. "$GITWEB_BUILD_OPTIONS"
    ++. "$GIT_VERSION_FILE"
     +
     +sed -e "1s|#!/usr/bin/perl|#!$PERL_PATH|" \
     +    -e "s|@PERL_PATH@|$PERL_PATH|" \
 8:  f2735c317c <  -:  ---------- Makefile: refactor GIT-VERSION-GEN to be reusable
 -:  ---------- > 11:  a9842baae7 Makefile: extract script to generate gitweb.js
 9:  e4fa8b74e8 = 12:  eb8c8eaf71 Makefile: refactor generators to be PWD-independent
10:  b1bbebf30d = 13:  8d74f3adde Makefile: allow "bin-wrappers/" directory to exist
11:  bee17be2a2 ! 14:  cda66f5f07 Makefile: simplify building of templates
    @@ contrib/buildsystems/CMakeLists.txt: include_directories(${CMAKE_BINARY_DIR})
      
      list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
      list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
    +@@ contrib/buildsystems/CMakeLists.txt: list(APPEND libgit_SOURCES "${CMAKE_BINARY_DIR}/version-def.h")
      add_library(libgit ${libgit_SOURCES} ${compat_SOURCES})
      
      #libxdiff
12:  b4cea04721 = 15:  d763b2c7bb Documentation: allow sourcing generated includes from separate dir
13:  56ba18676e = 16:  c3b0ce492c Documentation: teach "cmd-list.perl" about out-of-tree builds
14:  448f3aac9b = 17:  432cc11dea Documentation: extract script to generate a list of mergetools
15:  dd4ceab0d8 ! 18:  436f8d7dcb t: better support for out-of-tree builds
    @@ contrib/buildsystems/CMakeLists.txt: string(REPLACE "@GIT_PERF_MAKE_COMMAND@" ""
      string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
      string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
      string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
    -+string(REPLACE "@GIT_TEST_TEXTDOMAINDIR@" "${CMAKE_BINARY_DIR}/po/build/locale" git_build_options "${git_build_options}")
    -+string(REPLACE "@GIT_TEST_POPATH@" "${CMAKE_BINARY_DIR}/po" git_build_options "${git_build_options}")
    -+string(REPLACE "@GIT_TEST_TEMPLATE_DIR@" "${CMAKE_BINARY_DIR}/templates/blt" git_build_options "${git_build_options}")
    -+string(REPLACE "@GIT_TEST_GITPERLLIB@" "${CMAKE_BINARY_DIR}/perl/build/lib" git_build_options "${git_build_options}")
    -+string(REPLACE "@GIT_TEST_MERGE_TOOLS_DIR@" "${RUNTIME_PREFIX}" git_build_options "${git_build_options}")
    - string(REPLACE "@RUNTIME_PREFIX@" "${RUNTIME_PREFIX}" git_build_options "${git_build_options}")
    - string(REPLACE "@GITWEBDIR@" "${GITWEBDIR}" git_build_options "${git_build_options}")
    ++string(REPLACE "@GIT_TEST_TEXTDOMAINDIR@" "'${CMAKE_BINARY_DIR}/po/build/locale'" git_build_options "${git_build_options}")
    ++string(REPLACE "@GIT_TEST_POPATH@" "'${CMAKE_BINARY_DIR}/po'" git_build_options "${git_build_options}")
    ++string(REPLACE "@GIT_TEST_TEMPLATE_DIR@" "'${CMAKE_BINARY_DIR}/templates/blt'" git_build_options "${git_build_options}")
    ++string(REPLACE "@GIT_TEST_GITPERLLIB@" "'${CMAKE_BINARY_DIR}/perl/build/lib'" git_build_options "${git_build_options}")
    ++string(REPLACE "@GIT_TEST_MERGE_TOOLS_DIR@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
    + string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
    + string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
      string(REPLACE "@USE_GETTEXT_SCHEME@" "" git_build_options "${git_build_options}")
     
      ## t/lib-gettext.sh ##
16:  c1ac988650 = 19:  eba2e18294 t: allow overriding build dir
17:  fd053fe4a9 = 20:  0abdac86b1 Documentation: add comparison of build systems
18:  4c10499220 ! 21:  30055908e5 Introduce support for the Meson build system
    @@ gitweb/meson.build (new)
     +gitweb_config.set_quoted('PERL_PATH', perl.full_path())
     +gitweb_config.set_quoted('CSSMIN', '')
     +gitweb_config.set_quoted('JSMIN', '')
    -+gitweb_config.set_quoted('GIT_VERSION', git_version)
     +gitweb_config.set_quoted('GIT_BINDIR', get_option('prefix') / get_option('bindir'))
     +gitweb_config.set_quoted('GITWEB_CONFIG', get_option('gitweb_config'))
     +gitweb_config.set_quoted('GITWEB_CONFIG_SYSTEM', get_option('gitweb_config_system'))
    @@ gitweb/meson.build (new)
     +  output: 'gitweb.cgi',
     +  command: [
     +    shell,
    -+    meson.current_source_dir() / 'generate-gitweb.sh',
    ++    meson.current_source_dir() / 'generate-gitweb-cgi.sh',
     +    meson.current_build_dir() / 'GITWEB-BUILD-OPTIONS',
    ++    git_version_file.full_path(),
     +    '@INPUT@',
     +    '@OUTPUT@',
     +  ],
     +  install: true,
     +  install_dir: get_option('datadir') / 'gitweb',
    ++  depends: [git_version_file],
    ++)
    ++
    ++javascript_files = [
    ++  meson.current_source_dir() / 'static/js/adjust-timezone.js',
    ++  meson.current_source_dir() / 'static/js/blame_incremental.js',
    ++  meson.current_source_dir() / 'static/js/javascript-detection.js',
    ++  meson.current_source_dir() / 'static/js/lib/common-lib.js',
    ++  meson.current_source_dir() / 'static/js/lib/cookies.js',
    ++  meson.current_source_dir() / 'static/js/lib/datetime.js',
    ++]
    ++
    ++test_dependencies += custom_target(
    ++  input: javascript_files,
    ++  output: 'gitweb.js',
    ++  command: [
    ++    shell,
    ++    meson.current_source_dir() / 'generate-gitweb-js.sh',
    ++    '@OUTPUT@',
    ++  ] + javascript_files,
    ++  install: true,
    ++  install_dir: get_option('datadir') / 'gitweb/static',
     +)
     +
     +foreach asset : [
     +  'static/git-favicon.png',
     +  'static/git-logo.png',
     +  'static/gitweb.css',
    -+  'static/js/adjust-timezone.js',
    -+  'static/js/blame_incremental.js',
    -+  'static/js/javascript-detection.js',
    -+  'static/js/lib/common-lib.js',
    -+  'static/js/lib/cookies.js',
    -+  'static/js/lib/datetime.js',
     +]
     +  fs.copyfile(asset,
     +    install: true,
    @@ meson.build (new)
     +# Configuration
     +# =============
     +#
    -+# The exact configuration of Git is determined when setting up the Git
    -+# directory. Unless told otherwise, Meson will automatically detect the
    -+# availability of various bits and pieces. There are two different kinds of
    -+# options that can be used to further tweak the build:
    ++# The exact configuration of Git is determined when setting up the build
    ++# directory via `meson setup`. Unless told otherwise, Meson will automatically
    ++# detect the availability of various bits and pieces. There are two different
    ++# kinds of options that can be used to further tweak the build:
     +#
     +#   - Built-in options provided by Meson.
     +#
    @@ meson.build (new)
     +# Options can be configured either when setting up the build directory or can
     +# be changed in preexisting build directories:
     +#
    -+#      # Set up a build directory with optimized settings that will be
    ++#      # Set up a new build directory with optimized settings that will be
     +#      # installed into an alternative prefix.
    -+#      $ meson setup --buildtype release --optimization 3 --strip --prefix=/home/$USER
    ++#      $ meson setup --buildtype release --optimization 3 --strip --prefix=/home/$USER build
    ++#
    ++#      # Set up a new build directory with a higher warning level. Level 2 is
    ++#      # mostly equivalent to setting DEVELOPER=1, level 3 and "everything"
    ++#      # will enable even more warnings.
    ++#      $ meson setup -Dwarning_level=2
     +#
    -+#      # Set up a build directory with 'address' and 'undefined' sanitizers
    ++#      # Set up a new build directory with 'address' and 'undefined' sanitizers
     +#      # using Clang.
    -+#      $ CC=clang meson setup -Db_sanitize=address,undefined
    ++#      $ CC=clang meson setup -Db_sanitize=address,undefined build
     +#
     +#      # Disable tests in a preexisting build directory.
     +#      $ meson configure -Dtests=false
    @@ meson.build (new)
     +#      # Disable features based on Python
     +#      $ meson configure -Dpython=disabled
     +#
    -+#      # Disable features based on Python
    -+#      $ meson configure -Dpython=disabled
    -+#
     +# Options have a type like booleans, choices, strings or features. Features are
     +# somewhat special as they can have one of three values: enabled, disabled or
     +# auto. While the first two values are self-explanatory, "auto" will enable or
    @@ meson.build (new)
     +  script_environment.prepend('PATH', fs.parent(program.full_path()))
     +endforeach
     +
    -+git_version = run_command(shell, 'GIT-VERSION-GEN', check: false, env: script_environment).stdout().strip()
    -+if git_version == ''
    -+  git_version = meson.project_version()
    -+endif
    -+
     +compiler = meson.get_compiler('c')
     +
     +libgit_sources = [
    @@ meson.build (new)
     +build_options_config.set('GIT_TEST_OPTS', '')
     +build_options_config.set('GIT_TEST_PERL_FATAL_WARNINGS', '')
     +build_options_config.set('GIT_TEST_UTF8_LOCALE', '')
    -+build_options_config.set('SANITIZE_ADDRESS', '')
    -+build_options_config.set('SANITIZE_LEAK', '')
     +build_options_config.set('BROKEN_PATH_FIX', '')
     +build_options_config.set_quoted('LOCALEDIR', fs.as_posix(get_option('prefix') / get_option('localedir')))
     +build_options_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))
    @@ meson.build (new)
     +libgit_include_directories = [ '.' ]
     +libgit_dependencies = [ ]
     +
    ++# Treat any warning level above 1 the same as we treat DEVELOPER=1 in our
    ++# Makefile.
    ++if get_option('warning_level') in ['2','3', 'everything'] and compiler.get_argument_syntax() == 'gcc'
    ++  foreach cflag : [
    ++    '-Wdeclaration-after-statement',
    ++    '-Wformat-security',
    ++    '-Wold-style-definition',
    ++    '-Woverflow',
    ++    '-Wpointer-arith',
    ++    '-Wstrict-prototypes',
    ++    '-Wunused',
    ++    '-Wvla',
    ++    '-Wwrite-strings',
    ++    '-fno-common',
    ++    '-Wtautological-constant-out-of-range-compare',
    ++    # If a function is public, there should be a prototype and the right
    ++    # header file should be included. If not, it should be static.
    ++    '-Wmissing-prototypes',
    ++    # These are disabled because we have these all over the place.
    ++    '-Wno-empty-body',
    ++    '-Wno-missing-field-initializers',
    ++    '-Wno-sign-compare',
    ++  ]
    ++    if compiler.has_argument(cflag)
    ++      libgit_c_args += cflag
    ++    endif
    ++  endforeach
    ++endif
    ++
    ++if get_option('b_sanitize').contains('address')
    ++  build_options_config.set('SANITIZE_ADDRESS', 'YesCompiledWithIt')
    ++else
    ++  build_options_config.set('SANITIZE_ADDRESS', '')
    ++endif
    ++if get_option('b_sanitize').contains('leak')
    ++  libgit_c_args += '-DSUPPRESS_ANNOTATED_LEAKS'
    ++  build_options_config.set('SANITIZE_LEAK', 'YesCompiledWithIt')
    ++else
    ++  build_options_config.set('SANITIZE_LEAK', '')
    ++endif
    ++if get_option('b_sanitize').contains('undefined')
    ++  libgit_c_args += '-DSHA1DC_FORCE_ALIGNED_ACCESS'
    ++endif
    ++
     +executable_suffix = ''
     +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows'
     +  executable_suffix = '.exe'
    @@ meson.build (new)
     +  build_options_config.set('USE_LIBPCRE2', '')
     +endif
     +
    -+curl = dependency('libcurl', version: '>=7.21.3', required: get_option('curl'), default_options: ['default_library=static', 'tests=disabled'])
    ++curl = dependency('libcurl', version: '>=7.21.3', required: get_option('curl'), default_options: ['default_library=static', 'tests=disabled', 'tool=disabled'])
     +use_curl_for_imap_send = false
     +if curl.found()
     +  if curl.version().version_compare('>=7.34.0')
    @@ meson.build (new)
     +build_options_config.set_quoted('FSMONITOR_DAEMON_BACKEND', fsmonitor_backend)
     +build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_backend)
     +
    -+if compiler.has_header('regex.h') and compiler.get_define('REG_STARTEND', prefix: '#include <regex.h>') != ''
    ++if not get_option('b_sanitize').contains('address') and get_option('regex').allowed() and compiler.has_header('regex.h') and compiler.get_define('REG_STARTEND', prefix: '#include <regex.h>') != ''
     +  build_options_config.set('NO_REGEX', '')
     +
     +  if compiler.get_define('REG_ENHANCED', prefix: '#include <regex.h>') != ''
     +    libgit_c_args += '-DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS'
     +    libgit_sources += 'compat/regcomp_enhanced.c'
     +  endif
    -+else
    ++elif not get_option('regex').enabled()
     +  libgit_c_args += [
     +    '-DNO_REGEX',
     +    '-DGAWK',
    @@ meson.build (new)
     +  build_options_config.set('NO_REGEX', '1')
     +  libgit_sources += 'compat/regex/regex.c'
     +  libgit_include_directories += 'compat/regex'
    ++else
    ++    error('Native regex support requested but not found')
     +endif
     +
     +# setitimer and friends are provided by compat/mingw.c.
    @@ meson.build (new)
     +  libgit_c_args += '-DHAVE_CLOCK_MONOTONIC'
     +endif
     +
    -+if compiler.compiles('''
    ++if not compiler.compiles('''
     +  #include <inttypes.h>
     +
     +  void func(void)
    @@ meson.build (new)
     +  endif
     +endif
     +
    -+if compiler.run('''
    ++if not meson.is_cross_build() and compiler.run('''
     +  #include <stdio.h>
     +
     +  int main(int argc, const char **argv)
    @@ meson.build (new)
     +  configuration: build_options_config,
     +)
     +
    ++version_def_h = custom_target(
    ++  command: [
    ++    shell,
    ++    meson.current_source_dir() / 'GIT-VERSION-GEN',
    ++    meson.current_source_dir(),
    ++    '@INPUT@',
    ++    '@OUTPUT@',
    ++  ],
    ++  input: meson.current_source_dir() / 'version-def.h.in',
    ++  output: 'version-def.h',
    ++  build_always_stale: true,
    ++)
    ++
    ++git_version_file = custom_target(
    ++  command: [
    ++    shell,
    ++    meson.current_source_dir() / 'GIT-VERSION-GEN',
    ++    meson.current_source_dir(),
    ++    '@INPUT@',
    ++    '@OUTPUT@',
    ++  ],
    ++  input: meson.current_source_dir() / 'GIT-VERSION-FILE.in',
    ++  output: 'GIT-VERSION-FILE',
    ++  build_always_stale: true,
    ++)
    ++
     +# Build a separate library for "version.c" so that we do not have to rebuild
    -+# everything when the current Git commit changes. TODO: this only gets set up
    -+# at configuration time, so we do not notice version changes unless the build
    -+# instructions get regenerated. We should refactor the source file such that we
    -+# can substitute tags in the file via `vcs_tag()`.
    ++# everything when the current Git commit changes.
     +libgit_version_library = static_library('git-version',
    -+  sources: ['version.c'],
    -+  c_args: libgit_c_args + [
    -+    '-DGIT_VERSION="' + git_version + '"',
    -+    '-DGIT_USER_AGENT="' + 'git/' + git_version + '"',
    -+    '-DGIT_BUILT_FROM_COMMIT="' + run_command('git', 'rev-parse', '-q', '--verify', 'HEAD', check: false).stdout().strip() + '"',
    ++  sources: [
    ++    'version.c',
    ++    version_def_h,
     +  ],
    ++  c_args: libgit_c_args,
     +  dependencies: libgit_dependencies,
     +  include_directories: libgit_include_directories,
     +)
    @@ meson.build (new)
     +common_main_sources = ['common-main.c']
     +common_main_link_args = [ ]
     +if host_machine.system() == 'windows'
    -+  # TODO: wire these up properly.
    -+  common_main_sources += import('windows').compile_resources('git.rc', args: [
    -+    '-DMAJOR=1',
    -+    '-DMINOR=1',
    -+    '-DMICRO=1',
    -+    '-DPATCHLEVEL=0',
    -+    '-DGIT_VERSION=\\\"' + git_version + '.GIT\\\"',
    -+  ])
    ++  git_rc = custom_target(
    ++    command: [
    ++      shell,
    ++      meson.current_source_dir() / 'GIT-VERSION-GEN',
    ++      meson.current_source_dir(),
    ++      '@INPUT@',
    ++      '@OUTPUT@',
    ++    ],
    ++    input: meson.current_source_dir() / 'git.rc.in',
    ++    output: 'git.rc',
    ++    build_always_stale: true,
    ++  )
    ++
    ++  common_main_sources += import('windows').compile_resources(git_rc,
    ++    include_directories: [meson.current_source_dir()],
    ++  )
     +  if compiler.get_argument_syntax() == 'gcc'
     +    common_main_link_args += [
     +      '-municode',
    @@ meson.build (new)
     +
     +foreach symlink : [
     +  'git',
    -+  'git-http-backend',
     +  'git-receive-pack',
     +  'git-shell',
     +  'git-upload-archive',
    @@ meson.build (new)
     +    shell,
     +    meson.project_source_root() / 'generate-perl.sh',
     +    meson.project_build_root() / 'GIT-BUILD-OPTIONS',
    -+    git_version,
    ++    git_version_file.full_path(),
     +    perl_header,
     +    '@INPUT@',
     +    '@OUTPUT@',
    @@ meson.build (new)
     +      output: fs.stem(script),
     +      command: generate_perl_command,
     +      install: true,
    -+      install_dir: get_option('datadir') / 'perl5',
    ++      install_dir: get_option('libexecdir') / 'git-core',
    ++      depends: [git_version_file],
     +    )
     +    test_dependencies += generated_script
    ++
     +    if script == 'git-cvsserver.perl'
     +      bin_wrappers += generated_script
    ++
    ++      install_symlink(fs.stem(script),
    ++        install_dir: get_option('bindir'),
    ++        pointing_to: fs.relative_to(get_option('libexecdir') / 'git-core' / fs.stem(script), get_option('bindir')),
    ++      )
     +    endif
     +  endforeach
     +
    @@ meson.build (new)
     
      ## meson_options.txt (new) ##
     @@
    ++# Configuration for how Git behaves at runtime.
     +option('default_pager', type: 'string', value: 'less',
     +  description: 'Fall-back pager.')
     +option('default_editor', type: 'string', value: 'vi',
    @@ meson_options.txt (new)
     +option('runtime_prefix', type: 'boolean', value: false,
     +  description: 'Resolve ancillary tooling and support files relative to the location of the runtime binary instead of hard-coding them into the binary.')
     +
    ++# Features supported by Git.
     +option('curl', type: 'feature', value: 'enabled',
     +  description: 'Build helpers used to access remotes with the HTTP transport.')
     +option('expat', type: 'feature', value: 'enabled',
    @@ meson_options.txt (new)
     +  description: 'Install bundled copies of CPAN modules that serve as a fallback in case the modules are not available on the system.')
     +option('python', type: 'feature', value: 'auto',
     +  description: 'Build tools written in Python.')
    ++option('regex', type: 'feature', value: 'auto',
    ++  description: 'Use the system-provided regex library instead of the bundled one.')
     +
    ++# HTTPS backends.
     +option('openssl', type: 'feature', value: 'auto',
     +  description: 'Support access to HTTPS remotes. OpenSSL may still be pulled in if configured as SHA1 or SHA256 backend.')
     +option('CommonCrypto', type: 'feature', value: 'auto',
     +  description: 'Build tools written in Python.')
     +
    ++# Hashing backends.
     +option('sha1_backend', type: 'combo', choices: ['openssl', 'block', 'sha1dc', 'common-crypto'], value: 'sha1dc',
     +  description: 'The backend used for hashing objects with the SHA1 object format')
     +option('sha256_backend', type: 'combo', choices: ['openssl', 'nettle', 'gcrypt', 'block'], value: 'block',
     +  description: 'The backend used for hashing objects with the SHA256 object format')
     +
    ++# Build tweaks.
     +option('macos_use_homebrew_gettext', type: 'boolean', value: true,
     +  description: 'Use gettext from Homebrew instead of the slightly-broken system-provided one.')
     +
    ++# gitweb configuration.
     +option('gitweb_config', type: 'string', value: 'gitweb_config.perl')
     +option('gitweb_config_system', type: 'string', value: '/etc/gitweb.conf')
     +option('gitweb_config_common', type: 'string', value: '/etc/gitweb-common.conf')
    @@ meson_options.txt (new)
     +option('gitweb_site_footer', type: 'string', value: '')
     +option('highlight_bin', type: 'string', value: 'highlight')
     +
    ++# Documentation.
     +option('docs', type: 'array', choices: ['man', 'html'], value: [],
     +  description: 'Which documenattion formats to build and install.')
     +option('default_help_format', type: 'combo', choices: ['man', 'html'], value: 'man',
     +  description: 'Default format used when executing git-help(1).')
     +
    ++# Testing.
     +option('tests', type: 'boolean', value: true,
     +  description: 'Enable building tests. This requires Perl, but is separate from the "perl" option such that you can build tests without Perl features enabled.')
     +option('test_output_directory', type: 'string',
    @@ templates/hooks/meson.build (new)
     +    input: hook,
     +    output: hook,
     +    configuration: template_config,
    ++    install: true,
    ++    install_dir: get_option('datadir') / 'git-core/templates',
     +  )
     +endforeach
     
    @@ templates/info/meson.build (new)
     +  input: 'exclude',
     +  output: 'exclude',
     +  configuration: template_config,
    ++  install: true,
    ++  install_dir: get_option('datadir') / 'git-core/templates',
     +)
     
      ## templates/meson.build (new) ##
    @@ templates/meson.build (new)
     +  input: 'description',
     +  output: 'description',
     +  configuration: template_config,
    ++  install: true,
    ++  install_dir: get_option('datadir') / 'git-core/templates',
     +)
     +
     +subdir('hooks')
19:  88a7a7e38b = 22:  3bdf6240a5 meson: fix conflicts with in-flight topics

--- base-commit: b31fb630c0fc6869a33ed717163e8a1210460d94 change-id: 20241112-pks-meson-95b665c08f06

Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

The "GIT-BUILD-OPTIONS" file is generated by our build systems to propagate built-in features and paths to our tests. The generation is done ad-hoc, where both our Makefile and the CMake build instructions simply echo a bunch of strings into the file. This makes it very hard to figure out what variables are expected to exist and what format they have, and the written variables can easily get out of sync between build systems.

Introduce a new "GIT-BUILD-OPTIONS.in" template to address this issue. This has multiple advantages:

  - It demonstrates which built options exist in the first place.
  - It can serve as a spot to document the build options.
  - Some build systems complain when not all variables could be
    substituted, alerting us of mismatches. Others don't, but if we
    forgot to substitute such variables we now have a bogus string that
    will likely cause our tests to fail, if they have any meaning in the
    first place.

Backfill values that we didn't yet set in our CMake build instructions. While at it, remove the `SUPPORTS_SIMPLE_IPC` variable that we only set up in CMake as it isn't used anywhere.

This change requires us to adapt the setup of TEST_OUTPUT_DIRECTORY in "test-lib.sh" such that it does not get overwritten after sourcing when it has been set up via the environment. This is the only instance I could find where we rely on ordering on variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-BUILD-OPTIONS.in                |  36 ++++++++++++
 Makefile                            | 109 +++++++++++++-----------------------
 contrib/buildsystems/CMakeLists.txt |  58 ++++++++++++-------
 t/test-lib.sh                       |  22 ++++++--
 4 files changed, 130 insertions(+), 95 deletions(-)
Show changes to 4 files +130 −95

GIT-BUILD-OPTIONS.in, Makefile, contrib/buildsystems/CMakeLists.txt, t/test-lib.sh

diff --git a/GIT-BUILD-OPTIONS.in b/GIT-BUILD-OPTIONS.in
new file mode 100644
index 0000000000000000000000000000000000000000..f0ca240493c94aa41a6b6241a8474e42f7cdc8b9
--- /dev/null
+++ b/GIT-BUILD-OPTIONS.in
@@ -0,0 +1,36 @@
+SHELL_PATH=@SHELL_PATH@
+TEST_SHELL_PATH=@TEST_SHELL_PATH@
+PERL_PATH=@PERL_PATH@
+DIFF=@DIFF@
+PYTHON_PATH=@PYTHON_PATH@
+TAR=@TAR@
+NO_CURL=@NO_CURL@
+NO_ICONV=@NO_ICONV@
+NO_EXPAT=@NO_EXPAT@
+USE_LIBPCRE2=@USE_LIBPCRE2@
+NO_PERL=@NO_PERL@
+NO_PTHREADS=@NO_PTHREADS@
+NO_PYTHON=@NO_PYTHON@
+NO_REGEX=@NO_REGEX@
+NO_UNIX_SOCKETS=@NO_UNIX_SOCKETS@
+PAGER_ENV=@PAGER_ENV@
+SANITIZE_LEAK=@SANITIZE_LEAK@
+SANITIZE_ADDRESS=@SANITIZE_ADDRESS@
+X=@X@
+FSMONITOR_DAEMON_BACKEND=@FSMONITOR_DAEMON_BACKEND@
+FSMONITOR_OS_SETTINGS=@FSMONITOR_OS_SETTINGS@
+TEST_OUTPUT_DIRECTORY=@TEST_OUTPUT_DIRECTORY@
+GIT_TEST_OPTS=@GIT_TEST_OPTS@
+GIT_TEST_CMP=@GIT_TEST_CMP@
+GIT_TEST_CMP_USE_COPIED_CONTEXT=@GIT_TEST_CMP_USE_COPIED_CONTEXT@
+GIT_TEST_UTF8_LOCALE=@GIT_TEST_UTF8_LOCALE@
+NO_GETTEXT=@NO_GETTEXT@
+GIT_PERF_REPEAT_COUNT=@GIT_PERF_REPEAT_COUNT@
+GIT_PERF_REPO=@GIT_PERF_REPO@
+GIT_PERF_LARGE_REPO=@GIT_PERF_LARGE_REPO@
+GIT_PERF_MAKE_OPTS=@GIT_PERF_MAKE_OPTS@
+GIT_PERF_MAKE_COMMAND=@GIT_PERF_MAKE_COMMAND@
+GIT_INTEROP_MAKE_OPTS=@GIT_INTEROP_MAKE_OPTS@
+GIT_TEST_INDEX_VERSION=@GIT_TEST_INDEX_VERSION@
+GIT_TEST_PERL_FATAL_WARNINGS=@GIT_TEST_PERL_FATAL_WARNINGS@
+RUNTIME_PREFIX=@RUNTIME_PREFIX@
diff --git a/Makefile b/Makefile
index d06c9a8ffa7b637050c9619a367fbe61e7243a74..962df75635a8cbf7114970ddfe7948ff17c65fdc 100644
--- a/Makefile
+++ b/Makefile
@@ -3164,76 +3164,45 @@ GIT-LDFLAGS: FORCE
 # that runs GIT-BUILD-OPTIONS, and then again to protect it
 # and the first level quoting from the shell that runs "echo".
 GIT-BUILD-OPTIONS: FORCE
-	@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
-	@echo TEST_SHELL_PATH=\''$(subst ','\'',$(TEST_SHELL_PATH_SQ))'\' >>$@+
-	@echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
-	@echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
-	@echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
-	@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
-	@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
-	@echo NO_ICONV=\''$(subst ','\'',$(subst ','\'',$(NO_ICONV)))'\' >>$@+
-	@echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
-	@echo USE_LIBPCRE2=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE2)))'\' >>$@+
-	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
-	@echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
-	@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
-	@echo NO_REGEX=\''$(subst ','\'',$(subst ','\'',$(NO_REGEX)))'\' >>$@+
-	@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
-	@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
-	@echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+
-	@echo SANITIZE_ADDRESS=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_ADDRESS)))'\' >>$@+
-	@echo X=\'$(X)\' >>$@+
-ifdef FSMONITOR_DAEMON_BACKEND
-	@echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+
-endif
-ifdef FSMONITOR_OS_SETTINGS
-	@echo FSMONITOR_OS_SETTINGS=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_OS_SETTINGS)))'\' >>$@+
-endif
-ifdef TEST_OUTPUT_DIRECTORY
-	@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
-endif
-ifdef GIT_TEST_OPTS
-	@echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@+
-endif
-ifdef GIT_TEST_CMP
-	@echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@+
-endif
-ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
-	@echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@+
-endif
-ifdef GIT_TEST_UTF8_LOCALE
-	@echo GIT_TEST_UTF8_LOCALE=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_UTF8_LOCALE)))'\' >>$@+
-endif
-	@echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+
-ifdef GIT_PERF_REPEAT_COUNT
-	@echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@+
-endif
-ifdef GIT_PERF_REPO
-	@echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@+
-endif
-ifdef GIT_PERF_LARGE_REPO
-	@echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@+
-endif
-ifdef GIT_PERF_MAKE_OPTS
-	@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
-endif
-ifdef GIT_PERF_MAKE_COMMAND
-	@echo GIT_PERF_MAKE_COMMAND=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_COMMAND)))'\' >>$@+
-endif
-ifdef GIT_INTEROP_MAKE_OPTS
-	@echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
-endif
-ifdef GIT_TEST_INDEX_VERSION
-	@echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
-endif
-ifdef GIT_TEST_PERL_FATAL_WARNINGS
-	@echo GIT_TEST_PERL_FATAL_WARNINGS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_PERL_FATAL_WARNINGS)))'\' >>$@+
-endif
-ifdef RUNTIME_PREFIX
-	@echo RUNTIME_PREFIX=\'true\' >>$@+
-else
-	@echo RUNTIME_PREFIX=\'false\' >>$@+
-endif
+	@sed \
+		-e "s|@SHELL_PATH@|\'$(SHELL_PATH_SQ)\'|" \
+		-e "s|@TEST_SHELL_PATH@|\'$(TEST_SHELL_PATH_SQ)\'|" \
+		-e "s|@PERL_PATH@|\'$(PERL_PATH_SQ)\'|" \
+		-e "s|@DIFF@|\'$(DIFF)\'|" \
+		-e "s|@PYTHON_PATH@|\'$(PYTHON_PATH_SQ)\'|" \
+		-e "s|@TAR@|\'$(TAR)\'|" \
+		-e "s|@NO_CURL@|\'$(NO_CURL)\'|" \
+		-e "s|@NO_ICONV@|\'$(NO_ICONV)\'|" \
+		-e "s|@NO_EXPAT@|\'$(NO_EXPAT)\'|" \
+		-e "s|@USE_LIBPCRE2@|\'$(USE_LIBPCRE2)\'|" \
+		-e "s|@NO_PERL@|\'$(NO_PERL)\'|" \
+		-e "s|@NO_PTHREADS@|\'$(NO_PTHREADS)\'|" \
+		-e "s|@NO_PYTHON@|\'$(NO_PYTHON)\'|" \
+		-e "s|@NO_REGEX@|\'$(NO_REGEX)\'|" \
+		-e "s|@NO_UNIX_SOCKETS@|\'$(NO_UNIX_SOCKETS)\'|" \
+		-e "s|@PAGER_ENV@|\'$(PAGER_ENV)\'|" \
+		-e "s|@SANITIZE_LEAK@|\'$(SANITIZE_LEAK)\'|" \
+		-e "s|@SANITIZE_ADDRESS@|\'$(SANITIZE_ADDRESS)\'|" \
+		-e "s|@X@|\'$(X)\'|" \
+		-e "s|@FSMONITOR_DAEMON_BACKEND@|\'$(FSMONITOR_DAEMON_BACKEND)\'|" \
+		-e "s|@FSMONITOR_OS_SETTINGS@|\'$(FSMONITOR_OS_SETTINGS)\'|" \
+		-e "s|@TEST_OUTPUT_DIRECTORY@|\'$(TEST_OUTPUT_DIRECTORY)\'|" \
+		-e "s|@GIT_TEST_OPTS@|\'$(GIT_TEST_OPTS)\'|" \
+		-e "s|@GIT_TEST_CMP@|\'$(GIT_TEST_CMP)\'|" \
+		-e "s|@GIT_TEST_CMP_USE_COPIED_CONTEXT@|\'$(GIT_TEST_CMP_USE_COPIED_CONTEXT)\'|" \
+		-e "s|@GIT_TEST_UTF8_LOCALE@|\'$(GIT_TEST_UTF8_LOCALE)\'|" \
+		-e "s|@NO_GETTEXT@|\'$(NO_GETTEXT)\'|" \
+		-e "s|@GIT_PERF_REPEAT_COUNT@|\'$(GIT_PERF_REPEAT_COUNT)\'|" \
+		-e "s|@GIT_PERF_REPO@|\'$(GIT_PERF_REPO)\'|" \
+		-e "s|@GIT_PERF_LARGE_REPO@|\'$(GIT_PERF_LARGE_REPO)\'|" \
+		-e "s|@GIT_PERF_MAKE_OPTS@|\'$(GIT_PERF_MAKE_OPTS)\'|" \
+		-e "s|@GIT_PERF_MAKE_COMMAND@|\'$(GIT_PERF_MAKE_COMMAND)\'|" \
+		-e "s|@GIT_INTEROP_MAKE_OPTS@|\'$(GIT_INTEROP_MAKE_OPTS)\'|" \
+		-e "s|@GIT_TEST_INDEX_VERSION@|\'$(GIT_TEST_INDEX_VERSION)\'|" \
+		-e "s|@GIT_TEST_PERL_FATAL_WARNINGS@|\'$(GIT_TEST_PERL_FATAL_WARNINGS)\'|" \
+		-e "s|@RUNTIME_PREFIX@|\'$(RUNTIME_PREFIX)\'|" \
+		GIT-BUILD-OPTIONS.in >$@+
+	@if grep -q '^[A-Z][A-Z_]*=@.*@$$' $@+; then echo "Unsubstituted build options in $@" >&2 && exit 1; fi
 	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
 	@if test -f GIT-BUILD-DIR; then rm GIT-BUILD-DIR; fi
 
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 8974bb9fa202a0556fd9b16d105836d8cb66f543..e5d258419ddacd837c4abb07709fb3252fca5c3c 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1117,27 +1117,47 @@ if(NOT PYTHON_TESTS)
 	set(NO_PYTHON 1)
 endif()
 
-file(WRITE ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "SHELL_PATH='${SHELL_PATH}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "TEST_SHELL_PATH='${TEST_SHELL_PATH}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PERL_PATH='${PERL_PATH}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "DIFF='${DIFF}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PYTHON_PATH='${PYTHON_PATH}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "TAR='${TAR}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_CURL='${NO_CURL}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_ICONV='${NO_ICONV}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_EXPAT='${NO_EXPAT}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PERL='${NO_PERL}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PTHREADS='${NO_PTHREADS}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_UNIX_SOCKETS='${NO_UNIX_SOCKETS}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PAGER_ENV='${PAGER_ENV}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "X='${EXE_EXTENSION}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_GETTEXT='${NO_GETTEXT}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "RUNTIME_PREFIX='${RUNTIME_PREFIX}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PYTHON='${NO_PYTHON}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "SUPPORTS_SIMPLE_IPC='${SUPPORTS_SIMPLE_IPC}'\n")
+file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_CONSUME)
+string(REPLACE "@SHELL_PATH@" "'${SHELL_PATH}'" git_build_options "${git_build_options}")
+string(REPLACE "@TEST_SHELL_PATH@" "'${TEST_SHELL_PATH}'" git_build_options "${git_build_options}")
+string(REPLACE "@PERL_PATH@" "'${PERL_PATH}'" git_build_options "${git_build_options}")
+string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
+string(REPLACE "@PYTHON_PATH@" "'${PYTHON_PATH}'" git_build_options "${git_build_options}")
+string(REPLACE "@TAR@" "'${TAR}'" git_build_options "${git_build_options}")
+string(REPLACE "@NO_CURL@" "${NO_CURL}" git_build_options "${git_build_options}")
+string(REPLACE "@NO_ICONV@" "${NO_ICONV}" git_build_options "${git_build_options}")
+string(REPLACE "@NO_EXPAT@" "${NO_EXPAT}" git_build_options "${git_build_options}")
+string(REPLACE "@USE_LIBPCRE2@" "" git_build_options "${git_build_options}")
+string(REPLACE "@NO_PERL@" "${NO_PERL}" git_build_options "${git_build_options}")
+string(REPLACE "@NO_PTHREADS@" "${NO_PTHREADS}" git_build_options "${git_build_options}")
+string(REPLACE "@NO_PYTHON@" "${NO_PYTHON}" git_build_options "${git_build_options}")
+string(REPLACE "@NO_REGEX@" "" git_build_options "${git_build_options}")
+string(REPLACE "@NO_UNIX_SOCKETS@" "${NO_UNIX_SOCKETS}" git_build_options "${git_build_options}")
+string(REPLACE "@PAGER_ENV@" "'${PAGER_ENV}'" git_build_options "${git_build_options}")
+string(REPLACE "@SANITIZE_LEAK@" "" git_build_options "${git_build_options}")
+string(REPLACE "@SANITIZE_ADDRESS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@X@" "${EXE_EXTENSION}" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_DAEMON_BACKEND@" "win32" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_OS_SETTINGS@" "win32" git_build_options "${git_build_options}")
+string(REPLACE "@TEST_OUTPUT_DIRECTORY@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_OPTS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_CMP@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_CMP_USE_COPIED_CONTEXT@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_UTF8_LOCALE@" "" git_build_options "${git_build_options}")
+string(REPLACE "@NO_GETTEXT@" "${NO_GETTEXT}" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_PERF_REPEAT_COUNT@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_PERF_REPO@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_PERF_LARGE_REPO@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_PERF_MAKE_OPTS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_PERF_MAKE_COMMAND@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
 if(USE_VCPKG)
-	file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
+	string(APPEND git_build_options "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
 endif()
+file(WRITE ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS ${git_build_options})
 
 #Make the tests work when building out of the source tree
 get_filename_component(CACHE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../CMakeCache.txt ABSOLUTE)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index a278181a0568a2422ab1e7f007bc016b95a58e63..19057faa73925f619750fec30f05c3e5a1ba05ac 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -35,12 +35,6 @@ else
 	# needing to exist.
 	TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1
 fi
-if test -z "$TEST_OUTPUT_DIRECTORY"
-then
-	# Similarly, override this to store the test-results subdir
-	# elsewhere
-	TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
-fi
 GIT_BUILD_DIR="${TEST_DIRECTORY%/t}"
 if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR"
 then
@@ -92,6 +86,15 @@ export LSAN_OPTIONS
 prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS
 export UBSAN_OPTIONS
 
+# The TEST_OUTPUT_DIRECTORY will be overwritten via GIT-BUILD-OPTIONS. So in
+# case the caller has manually set up this variable via the environment we must
+# make sure to not overwrite that value, and thus we save it into
+# TEST_OUTPUT_DIRECTORY_OVERRIDE here.
+if test -n "$TEST_OUTPUT_DIRECTORY" && test -z "$TEST_OUTPUT_DIRECTORY_OVERRIDE"
+then
+	TEST_OUTPUT_DIRECTORY_OVERRIDE=$TEST_OUTPUT_DIRECTORY
+fi
+
 if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 then
 	echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).'
@@ -100,6 +103,13 @@ fi
 . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 export PERL_PATH SHELL_PATH
 
+if test -z "$TEST_OUTPUT_DIRECTORY"
+then
+	# Similarly, override this to store the test-results subdir
+	# elsewhere
+	TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
+fi
+
 # In t0000, we need to override test directories of nested testcases. In case
 # the developer has TEST_OUTPUT_DIRECTORY part of his build options, then we'd
 # reset this value to instead contain what the developer has specified. We thus
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 02/22] Makefile: consistently use @PLACEHOLDER@ to substitute

We have a bunch of placeholders in our scripts that we replace at build time, for example by using sed(1). These placeholders come in three different formats: @PLACEHOLDER@, @@PLACEHOLDER@@ and ++PLACEHOLDER++.

Next to being inconsistent it also creates a bit of a problem with CMake, which only supports the first syntax in its `configure_file()` function. To work around that we instead manually replace placeholders via string operations, which is a hassle and removes safeguards that CMake has to verify that we didn't forget to replace any placeholders. Besides that, other build systems like Meson also support the CMake syntax.

Unify our codebase to consistently use the syntax supported by such build systems.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Makefile                                         | 44 ++++++++++++------------
 configure.ac                                     |  2 +-
 contrib/buildsystems/CMakeLists.txt              | 34 +++++++++---------
 git-cvsserver.perl                               |  2 +-
 git-instaweb.sh                                  |  8 ++---
 git-request-pull.sh                              |  2 +-
 git-send-email.perl                              |  2 +-
 git-sh-i18n.sh                                   |  6 ++--
 git-sh-setup.sh                                  |  6 ++--
 git-svn.perl                                     |  2 +-
 gitk-git/po/vi.po                                |  2 +-
 gitweb/Makefile                                  | 44 ++++++++++++------------
 gitweb/gitweb.perl                               | 44 ++++++++++++------------
 perl/Git/I18N.pm                                 |  6 ++--
 perl/Git/LoadCPAN.pm                             |  6 ++--
 perl/header_templates/fixed_prefix.template.pl   |  2 +-
 perl/header_templates/runtime_prefix.template.pl |  8 ++---
 unimplemented.sh                                 |  2 +-
 wrap-for-bin.sh                                  | 18 +++++-----
 19 files changed, 120 insertions(+), 120 deletions(-)
Show changes to 19 files +120 −120

Makefile, configure.ac, contrib/buildsystems/CMakeLists.txt, git-cvsserver.perl, git-instaweb.sh, git-request-pull.sh, git-send-email.perl, git-sh-i18n.sh, git-sh-setup.sh, git-svn.perl, gitk-git/po/vi.po, gitweb/Makefile, gitweb/gitweb.perl, perl/Git/I18N.pm, perl/Git/LoadCPAN.pm, perl/header_templates/fixed_prefix.template.pl, perl/header_templates/runtime_prefix.template.pl, unimplemented.sh, wrap-for-bin.sh

diff --git a/Makefile b/Makefile
index 962df75635a8cbf7114970ddfe7948ff17c65fdc..d59f6c16868e24a1e13b0c16cc05adb79f81bbc8 100644
--- a/Makefile
+++ b/Makefile
@@ -1555,10 +1555,10 @@ endif
 
 ifdef SANE_TOOL_PATH
 SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
-BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
+BROKEN_PATH_FIX = 's|^\# @BROKEN_PATH_FIX@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
 PATH := $(SANE_TOOL_PATH):${PATH}
 else
-BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
+BROKEN_PATH_FIX = '/^\# @BROKEN_PATH_FIX@$$/d'
 endif
 
 ifeq (,$(HOST_CPU))
@@ -2548,13 +2548,13 @@ GIT-SCRIPT-DEFINES: FORCE
 define cmd_munge_script
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-    -e 's|@@DIFF@@|$(DIFF_SQ)|' \
-    -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
-    -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
+    -e 's|@DIFF@|$(DIFF_SQ)|' \
+    -e 's|@LOCALEDIR@|$(localedir_SQ)|g' \
+    -e 's/@USE_GETTEXT_SCHEME@/$(USE_GETTEXT_SCHEME)/g' \
     -e $(BROKEN_PATH_FIX) \
-    -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-    -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
+    -e 's|@GITWEBDIR@|$(gitwebdir_SQ)|g' \
+    -e 's|@PERL@|$(PERL_PATH_SQ)|g' \
+    -e 's|@PAGER_ENV@|$(PAGER_ENV_SQ)|g' \
     $@.sh >$@+
 endef
 
@@ -2611,7 +2611,7 @@ $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
 	    -e '	r GIT-PERL-HEADER' \
 	    -e '	G' \
 	    -e '}' \
-	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+	    -e 's/@GIT_VERSION@/$(GIT_VERSION)/g' \
 	    $< >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
@@ -2629,11 +2629,11 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
 	INSTLIBDIR='$(perllibdir_SQ)' && \
 	INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
 	INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
-	sed -e 's=@@PATHSEP@@=$(pathsep)=g' \
-	    -e "s=@@INSTLIBDIR@@=$$INSTLIBDIR=g" \
-	    -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \
-	    -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \
-	    -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \
+	sed -e 's=@PATHSEP@=$(pathsep)=g' \
+	    -e "s=@INSTLIBDIR@=$$INSTLIBDIR=g" \
+	    -e 's=@PERLLIBDIR_REL@=$(perllibdir_relative_SQ)=g' \
+	    -e 's=@GITEXECDIR_REL@=$(gitexecdir_relative_SQ)=g' \
+	    -e 's=@LOCALEDIR_REL@=$(localedir_relative_SQ)=g' \
 	    $< >$@+ && \
 	mv $@+ $@
 
@@ -2649,7 +2649,7 @@ else # NO_PERL
 $(SCRIPT_PERL_GEN) git-instaweb: % : unimplemented.sh
 	$(QUIET_GEN) \
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-	    -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
+	    -e 's|@REASON@|NO_PERL=$(NO_PERL)|g' \
 	    unimplemented.sh >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
@@ -2670,13 +2670,13 @@ else # NO_PYTHON
 $(SCRIPT_PYTHON_GEN): % : unimplemented.sh
 	$(QUIET_GEN) \
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-	    -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
+	    -e 's|@REASON@|NO_PYTHON=$(NO_PYTHON)|g' \
 	    unimplemented.sh >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
 endif # NO_PYTHON
 
-CONFIGURE_RECIPE = sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+CONFIGURE_RECIPE = sed -e 's/@GIT_VERSION@/$(GIT_VERSION)/g' \
 			configure.ac >configure.ac+ && \
 		   autoconf -o configure configure.ac+ && \
 		   $(RM) configure.ac+
@@ -3104,9 +3104,9 @@ endif
 perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
 	$(call mkdir_p_parent_template)
 	$(QUIET_GEN) \
-	sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
-	    -e 's|@@NO_GETTEXT@@|$(NO_GETTEXT_SQ)|g' \
-	    -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
+	sed -e 's|@LOCALEDIR@|$(perl_localedir_SQ)|g' \
+	    -e 's|@NO_GETTEXT@|$(NO_GETTEXT_SQ)|g' \
+	    -e 's|@NO_PERL_CPAN_FALLBACKS@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
 	< $< > $@
 
 perl/build/man/man3/Git.3pm: perl/Git.pm
@@ -3225,8 +3225,8 @@ all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(CLAR_TEST_PR
 bin-wrappers/%: wrap-for-bin.sh
 	$(call mkdir_p_parent_template)
 	$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-	     -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
-	     -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))$(if $(filter-out $(BINDIR_PROGRAMS_NO_X),$(@F)),$(X),)|' < $< > $@ && \
+	     -e 's|@BUILD_DIR@|$(shell pwd)|' \
+	     -e 's|@PROG@|$(patsubst test-%,t/helper/test-%,$(@F))$(if $(filter-out $(BINDIR_PROGRAMS_NO_X),$(@F)),$(X),)|' < $< > $@ && \
 	chmod +x $@
 
 # GNU make supports exporting all variables by "export" without parameters.
diff --git a/configure.ac b/configure.ac
index d1a96da14eb56766e1538da80ab97ed8a99b24f6..5923edc44aa7b6cc355fa6589b6d075b0c0a7772 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,7 +142,7 @@ fi
 ## Configure body starts here.
 
 AC_PREREQ(2.59)
-AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
+AC_INIT([git], [@GIT_VERSION@], [git@vger.kernel.org])
 
 AC_CONFIG_SRCDIR([git.c])
 
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index e5d258419ddacd837c4abb07709fb3252fca5c3c..8b0020edeba289e5eaa15a5452013fa2e4c3ed33 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -836,14 +836,14 @@ set(git_shell_scripts
 foreach(script ${git_shell_scripts})
 	file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.sh content NEWLINE_CONSUME)
 	string(REPLACE "@SHELL_PATH@" "${SHELL_PATH}" content "${content}")
-	string(REPLACE "@@DIFF@@" "diff" content "${content}")
+	string(REPLACE "@DIFF@" "diff" content "${content}")
 	string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" content "${content}")
 	string(REPLACE "@GITWEBDIR@" "${GITWEBDIR}" content "${content}")
-	string(REPLACE "@@NO_CURL@@" "" content "${content}")
-	string(REPLACE "@@USE_GETTEXT_SCHEME@@" "" content "${content}")
-	string(REPLACE "# @@BROKEN_PATH_FIX@@" "" content "${content}")
-	string(REPLACE "@@PERL@@" "${PERL_PATH}" content "${content}")
-	string(REPLACE "@@PAGER_ENV@@" "LESS=FRX LV=-c" content "${content}")
+	string(REPLACE "@NO_CURL@" "" content "${content}")
+	string(REPLACE "@USE_GETTEXT_SCHEME@" "" content "${content}")
+	string(REPLACE "# @BROKEN_PATH_FIX@" "" content "${content}")
+	string(REPLACE "@PERL@" "${PERL_PATH}" content "${content}")
+	string(REPLACE "@PAGER_ENV@" "LESS=FRX LV=-c" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
 endforeach()
 
@@ -852,13 +852,13 @@ parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" ".perl")
 
 #create perl header
 file(STRINGS ${CMAKE_SOURCE_DIR}/perl/header_templates/fixed_prefix.template.pl perl_header )
-string(REPLACE "@@PATHSEP@@" ":" perl_header "${perl_header}")
-string(REPLACE "@@INSTLIBDIR@@" "${INSTLIBDIR}" perl_header "${perl_header}")
+string(REPLACE "@PATHSEP@" ":" perl_header "${perl_header}")
+string(REPLACE "@INSTLIBDIR@" "${INSTLIBDIR}" perl_header "${perl_header}")
 
 foreach(script ${git_perl_scripts})
 	file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.perl content NEWLINE_CONSUME)
 	string(REPLACE "#!/usr/bin/perl" "#!/usr/bin/perl\n${perl_header}\n" content "${content}")
-	string(REPLACE "@@GIT_VERSION@@" "${PROJECT_VERSION}" content "${content}")
+	string(REPLACE "@GIT_VERSION@" "${PROJECT_VERSION}" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
 endforeach()
 
@@ -873,8 +873,8 @@ file(GLOB_RECURSE perl_modules "${CMAKE_SOURCE_DIR}/perl/*.pm")
 foreach(pm ${perl_modules})
 	string(REPLACE "${CMAKE_SOURCE_DIR}/perl/" "" file_path ${pm})
 	file(STRINGS ${pm} content NEWLINE_CONSUME)
-	string(REPLACE "@@LOCALEDIR@@" "${LOCALEDIR}" content "${content}")
-	string(REPLACE "@@NO_PERL_CPAN_FALLBACKS@@" "" content "${content}")
+	string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" content "${content}")
+	string(REPLACE "@NO_PERL_CPAN_FALLBACKS@" "" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/perl/build/lib/${file_path} ${content})
 #test-lib.sh requires perl/build/lib to be the build directory of perl modules
 endforeach()
@@ -1056,21 +1056,21 @@ set(wrapper_test_scripts
 
 foreach(script ${wrapper_scripts})
 	file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
-	string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
-	string(REPLACE "@@PROG@@" "${script}${EXE_EXTENSION}" content "${content}")
+	string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
+	string(REPLACE "@PROG@" "${script}${EXE_EXTENSION}" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
 endforeach()
 
 foreach(script ${wrapper_test_scripts})
 	file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
-	string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
-	string(REPLACE "@@PROG@@" "t/helper/${script}${EXE_EXTENSION}" content "${content}")
+	string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
+	string(REPLACE "@PROG@" "t/helper/${script}${EXE_EXTENSION}" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
 endforeach()
 
 file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
-string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
-string(REPLACE "@@PROG@@" "git-cvsserver" content "${content}")
+string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
+string(REPLACE "@PROG@" "git-cvsserver" content "${content}")
 file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})
 
 #options for configuring test options
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 124f598bdc07058b139f8dcbf8245128c52d9176..70ae7cb8e456885ddf2b13a70d21831cdaa7bf06 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -26,7 +26,7 @@
 use File::Basename;
 use Getopt::Long qw(:config require_order no_ignore_case);
 
-my $VERSION = '@@GIT_VERSION@@';
+my $VERSION = '@GIT_VERSION@';
 
 my $log = GITCVS::log->new();
 my $cfg;
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 8dbe21d5887595352bc0159ea9befdf296b8105f..c8efb1205a8e2a66a6aced840980978897e122bc 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-PERL='@@PERL@@'
+PERL='@PERL@'
 OPTIONS_KEEPDASHDASH=
 OPTIONS_STUCKLONG=
 OPTIONS_SPEC="\
@@ -38,8 +38,8 @@ conf="$GIT_DIR/gitweb/httpd.conf"
 # if installed, it doesn't need further configuration (module_path)
 test -z "$httpd" && httpd='lighttpd -f'
 
-# Default is @@GITWEBDIR@@
-test -z "$root" && root='@@GITWEBDIR@@'
+# Default is @GITWEBDIR@
+test -z "$root" && root='@GITWEBDIR@'
 
 # any untaken local port will do...
 test -z "$port" && port=1234
@@ -716,7 +716,7 @@ EOF
 
 gitweb_conf() {
 	cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
-#!@@PERL@@
+#!@PERL@
 our \$projectroot = "$(dirname "$fqgitdir")";
 our \$git_temp = "$fqgitdir/gitweb/tmp";
 our \$projects_list = \$projectroot;
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 01640a044bb10f4167338fca957c43a240e1b6c0..10c88639e28c02650ffea3d4671afa9a9f0bb182 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -112,7 +112,7 @@ find_matching_ref='
 	}
 '
 
-set fnord $(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+set fnord $(git ls-remote "$url" | @PERL@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
 remote_sha1=$2
 ref=$3
 
diff --git a/git-send-email.perl b/git-send-email.perl
index c835d4c11af259242f1128101a15b237302c7f42..02805c43e52570fe5ae5432fb9343ce9165cc16b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1501,7 +1501,7 @@ sub gen_header {
 	@recipients = unique_email_list(@recipients,@cc,@initial_bcc);
 	@recipients = (map { extract_valid_address_or_die($_) } @recipients);
 	my $date = format_2822_time($time++);
-	my $gitversion = '@@GIT_VERSION@@';
+	my $gitversion = '@GIT_VERSION@';
 	if ($gitversion =~ m/..GIT_VERSION../) {
 	    $gitversion = Git::version();
 	}
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
index a15c0620db6893898152b1c9e363a0af7f1b304e..ae4b2d6ba9dc061ef40ef1867f0cd5e975df8fe5 100644
--- a/git-sh-i18n.sh
+++ b/git-sh-i18n.sh
@@ -9,7 +9,7 @@ TEXTDOMAIN=git
 export TEXTDOMAIN
 if test -z "$GIT_TEXTDOMAINDIR"
 then
-	TEXTDOMAINDIR="@@LOCALEDIR@@"
+	TEXTDOMAINDIR="@LOCALEDIR@"
 else
 	TEXTDOMAINDIR="$GIT_TEXTDOMAINDIR"
 fi
@@ -17,9 +17,9 @@ export TEXTDOMAINDIR
 
 # First decide what scheme to use...
 GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough
-if test -n "@@USE_GETTEXT_SCHEME@@"
+if test -n "@USE_GETTEXT_SCHEME@"
 then
-	GIT_INTERNAL_GETTEXT_SH_SCHEME="@@USE_GETTEXT_SCHEME@@"
+	GIT_INTERNAL_GETTEXT_SH_SCHEME="@USE_GETTEXT_SCHEME@"
 elif test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
 then
 	: no probing necessary
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index ce273fe0e48d997cb067e77707026616a9302eb2..19aef72ec2553027bb25a5968d4e428ba986fdb2 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -41,7 +41,7 @@ git_broken_path_fix () {
 	esac
 }
 
-# @@BROKEN_PATH_FIX@@
+# @BROKEN_PATH_FIX@
 
 # Source git-sh-i18n for gettext support.
 . "$(git --exec-path)/git-sh-i18n"
@@ -154,7 +154,7 @@ git_pager() {
 	else
 		GIT_PAGER=cat
 	fi
-	for vardef in @@PAGER_ENV@@
+	for vardef in @PAGER_ENV@
 	do
 		var=${vardef%%=*}
 		eval ": \"\${$vardef}\" && export $var"
@@ -280,7 +280,7 @@ get_author_ident_from_commit () {
 # remove lines from $1 that are not in $2, leaving only common lines.
 create_virtual_base() {
 	sz0=$(wc -c <"$1")
-	@@DIFF@@ -u -La/"$1" -Lb/"$1" "$1" "$2" | git apply --no-add
+	@DIFF@ -u -La/"$1" -Lb/"$1" "$1" "$2" | git apply --no-add
 	sz1=$(wc -c <"$1")
 
 	# If we do not have enough common material, it is not
diff --git a/git-svn.perl b/git-svn.perl
index 01e7a70de1c0eb37b3feefe364ad78d4c8973097..dcf65cf1d1d4dc53b9f813f931ecff1a387c2657 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -9,7 +9,7 @@
 		$_revision $_repository
 		$_q $_authors $_authors_prog %users/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '@@GIT_VERSION@@';
+$VERSION = '@GIT_VERSION@';
 
 use Carp qw/croak/;
 use File::Basename qw/dirname basename/;
diff --git a/gitk-git/po/vi.po b/gitk-git/po/vi.po
index 59674986604891d9901e87c163e61dd39db14184..184430dcdeea7cbb76895840f84a889b6abc20bf 100644
--- a/gitk-git/po/vi.po
+++ b/gitk-git/po/vi.po
@@ -5,7 +5,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gitk @@GIT_VERSION@@\n"
+"Project-Id-Version: gitk @GIT_VERSION@\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2015-05-17 14:32+1000\n"
 "PO-Revision-Date: 2015-09-15 07:33+0700\n"
diff --git a/gitweb/Makefile b/gitweb/Makefile
index 3b68ab2d672c4afae62f8f4dc024e0911027b58d..164c8d53757f98599ad14eeb22ca8d542eb7502a 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -79,28 +79,28 @@ GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
 
 GITWEB_REPLACE = \
-	-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-	-e 's|++GIT_BINDIR++|$(bindir)|g' \
-	-e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
-	-e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
-	-e 's|++GITWEB_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|g' \
-	-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-	-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-	-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
-	-e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
-	-e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
-	-e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
-	-e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
-	-e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
-	-e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
-	-e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
-	-e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
-	-e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
-	-e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
-	-e 's|++GITWEB_SITE_HTML_HEAD_STRING++|$(GITWEB_SITE_HTML_HEAD_STRING)|g' \
-	-e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
-	-e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
-	-e 's|++HIGHLIGHT_BIN++|$(HIGHLIGHT_BIN)|g'
+	-e 's|@GIT_VERSION@|$(GIT_VERSION)|g' \
+	-e 's|@GIT_BINDIR@|$(bindir)|g' \
+	-e 's|@GITWEB_CONFIG@|$(GITWEB_CONFIG)|g' \
+	-e 's|@GITWEB_CONFIG_SYSTEM@|$(GITWEB_CONFIG_SYSTEM)|g' \
+	-e 's|@GITWEB_CONFIG_COMMON@|$(GITWEB_CONFIG_COMMON)|g' \
+	-e 's|@GITWEB_HOME_LINK_STR@|$(GITWEB_HOME_LINK_STR)|g' \
+	-e 's|@GITWEB_SITENAME@|$(GITWEB_SITENAME)|g' \
+	-e 's|@GITWEB_PROJECTROOT@|$(GITWEB_PROJECTROOT)|g' \
+	-e 's|"@GITWEB_PROJECT_MAXDEPTH@"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
+	-e 's|@GITWEB_EXPORT_OK@|$(GITWEB_EXPORT_OK)|g' \
+	-e 's|@GITWEB_STRICT_EXPORT@|$(GITWEB_STRICT_EXPORT)|g' \
+	-e 's|@GITWEB_BASE_URL@|$(GITWEB_BASE_URL)|g' \
+	-e 's|@GITWEB_LIST@|$(GITWEB_LIST)|g' \
+	-e 's|@GITWEB_HOMETEXT@|$(GITWEB_HOMETEXT)|g' \
+	-e 's|@GITWEB_CSS@|$(GITWEB_CSS)|g' \
+	-e 's|@GITWEB_LOGO@|$(GITWEB_LOGO)|g' \
+	-e 's|@GITWEB_FAVICON@|$(GITWEB_FAVICON)|g' \
+	-e 's|@GITWEB_JS@|$(GITWEB_JS)|g' \
+	-e 's|@GITWEB_SITE_HTML_HEAD_STRING@|$(GITWEB_SITE_HTML_HEAD_STRING)|g' \
+	-e 's|@GITWEB_SITE_HEADER@|$(GITWEB_SITE_HEADER)|g' \
+	-e 's|@GITWEB_SITE_FOOTER@|$(GITWEB_SITE_FOOTER)|g' \
+	-e 's|@HIGHLIGHT_BIN@|$(HIGHLIGHT_BIN)|g'
 
 .PHONY: FORCE
 $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS: FORCE
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b09a8d052383fdf1190f1fab008ddef99290e1f0..76e1f4e244fd23dd29b935e83753efa9c6d5bb5e 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -35,7 +35,7 @@ BEGIN
 	CGI->compile() if $ENV{'MOD_PERL'};
 }
 
-our $version = "++GIT_VERSION++";
+our $version = "@GIT_VERSION@";
 
 our ($my_url, $my_uri, $base_url, $path_info, $home_link);
 sub evaluate_uri {
@@ -80,46 +80,46 @@ sub evaluate_uri {
 
 # core git executable to use
 # this can just be "git" if your webserver has a sensible PATH
-our $GIT = "++GIT_BINDIR++/git";
+our $GIT = "@GIT_BINDIR@/git";
 
 # absolute fs-path which will be prepended to the project path
 #our $projectroot = "/pub/scm";
-our $projectroot = "++GITWEB_PROJECTROOT++";
+our $projectroot = "@GITWEB_PROJECTROOT@";
 
 # fs traversing limit for getting project list
 # the number is relative to the projectroot
-our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
+our $project_maxdepth = "@GITWEB_PROJECT_MAXDEPTH@";
 
 # string of the home link on top of all pages
-our $home_link_str = "++GITWEB_HOME_LINK_STR++";
+our $home_link_str = "@GITWEB_HOME_LINK_STR@";
 
 # extra breadcrumbs preceding the home link
 our @extra_breadcrumbs = ();
 
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
-our $site_name = "++GITWEB_SITENAME++"
+our $site_name = "@GITWEB_SITENAME@"
                  || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
 
 # html snippet to include in the <head> section of each page
-our $site_html_head_string = "++GITWEB_SITE_HTML_HEAD_STRING++";
+our $site_html_head_string = "@GITWEB_SITE_HTML_HEAD_STRING@";
 # filename of html text to include at top of each page
-our $site_header = "++GITWEB_SITE_HEADER++";
+our $site_header = "@GITWEB_SITE_HEADER@";
 # html text to include at home page
-our $home_text = "++GITWEB_HOMETEXT++";
+our $home_text = "@GITWEB_HOMETEXT@";
 # filename of html text to include at bottom of each page
-our $site_footer = "++GITWEB_SITE_FOOTER++";
+our $site_footer = "@GITWEB_SITE_FOOTER@";
 
 # URI of stylesheets
-our @stylesheets = ("++GITWEB_CSS++");
+our @stylesheets = ("@GITWEB_CSS@");
 # URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
 our $stylesheet = undef;
 # URI of GIT logo (72x27 size)
-our $logo = "++GITWEB_LOGO++";
+our $logo = "@GITWEB_LOGO@";
 # URI of GIT favicon, assumed to be image/png type
-our $favicon = "++GITWEB_FAVICON++";
+our $favicon = "@GITWEB_FAVICON@";
 # URI of gitweb.js (JavaScript code for gitweb)
-our $javascript = "++GITWEB_JS++";
+our $javascript = "@GITWEB_JS@";
 
 # URI and label (title) of GIT logo link
 #our $logo_url = "https://www.kernel.org/pub/software/scm/git/docs/";
@@ -128,7 +128,7 @@ sub evaluate_uri {
 our $logo_label = "git homepage";
 
 # source of projects list
-our $projects_list = "++GITWEB_LIST++";
+our $projects_list = "@GITWEB_LIST@";
 
 # the width (in characters) of the projects list "Description" column
 our $projects_list_description_width = 25;
@@ -147,7 +147,7 @@ sub evaluate_uri {
 
 # show repository only if this file exists
 # (only effective if this variable evaluates to true)
-our $export_ok = "++GITWEB_EXPORT_OK++";
+our $export_ok = "@GITWEB_EXPORT_OK@";
 
 # don't generate age column on the projects list page
 our $omit_age_column = 0;
@@ -161,11 +161,11 @@ sub evaluate_uri {
 our $export_auth_hook = undef;
 
 # only allow viewing of repositories also shown on the overview page
-our $strict_export = "++GITWEB_STRICT_EXPORT++";
+our $strict_export = "@GITWEB_STRICT_EXPORT@";
 
 # list of git base URLs used for URL to where fetch project from,
 # i.e. full URL is "$git_base_url/$project"
-our @git_base_url_list = grep { $_ ne '' } ("++GITWEB_BASE_URL++");
+our @git_base_url_list = grep { $_ ne '' } ("@GITWEB_BASE_URL@");
 
 # default blob_plain mimetype and default charset for text/plain blob
 our $default_blob_plain_mimetype = 'text/plain';
@@ -200,7 +200,7 @@ sub evaluate_uri {
 # http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
 # Useful if highlight is not installed on your webserver's PATH.
 # [Default: highlight]
-our $highlight_bin = "++HIGHLIGHT_BIN++";
+our $highlight_bin = "@HIGHLIGHT_BIN@";
 
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
@@ -741,9 +741,9 @@ sub read_config_file {
 
 our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
 sub evaluate_gitweb_config {
-	our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
-	our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-	our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
+	our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "@GITWEB_CONFIG@";
+	our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "@GITWEB_CONFIG_SYSTEM@";
+	our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "@GITWEB_CONFIG_COMMON@";
 
 	# Protect against duplications of file names, to not read config twice.
 	# Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index 475e90a6df5a272d6b4d557d8d37d68e3f77dfa8..f8f0ca312543ce2d099f16354e62acbc205abe5a 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -20,14 +20,14 @@ BEGIN
 # this "'@@' [...] '@@'" pattern.
 use constant NO_GETTEXT_STR => '@@' . 'NO_GETTEXT' . '@@';
 use constant NO_GETTEXT => (
-	q[@@NO_GETTEXT@@] ne ''
+	q[@NO_GETTEXT@] ne ''
 	and
-	q[@@NO_GETTEXT@@] ne NO_GETTEXT_STR
+	q[@NO_GETTEXT@] ne NO_GETTEXT_STR
 );
 
 sub __bootstrap_locale_messages {
 	our $TEXTDOMAIN = 'git';
-	our $TEXTDOMAINDIR ||= $ENV{GIT_TEXTDOMAINDIR} || '@@LOCALEDIR@@';
+	our $TEXTDOMAINDIR ||= $ENV{GIT_TEXTDOMAINDIR} || '@LOCALEDIR@';
 	die "NO_GETTEXT=" . NO_GETTEXT_STR if NO_GETTEXT;
 
 	require POSIX;
diff --git a/perl/Git/LoadCPAN.pm b/perl/Git/LoadCPAN.pm
index 8c7fa805f97390a47366b0d0058966ee391b9df1..6be99840f84fb2fe945f969fb23f73454c6b171b 100644
--- a/perl/Git/LoadCPAN.pm
+++ b/perl/Git/LoadCPAN.pm
@@ -31,11 +31,11 @@ =head1 DESCRIPTION
 # Makefile, and allows for detecting whether the module is loaded from
 # perl/Git as opposed to perl/build/Git, which is useful for one-off
 # testing without having Error.pm et al installed.
-use constant NO_PERL_CPAN_FALLBACKS_STR => '@@' . 'NO_PERL_CPAN_FALLBACKS' . '@@';
+use constant NO_PERL_CPAN_FALLBACKS_STR => '@' . 'NO_PERL_CPAN_FALLBACKS' . '@';
 use constant NO_PERL_CPAN_FALLBACKS => (
-	q[@@NO_PERL_CPAN_FALLBACKS@@] ne ''
+	q[@NO_PERL_CPAN_FALLBACKS@] ne ''
 	and
-	q[@@NO_PERL_CPAN_FALLBACKS@@] ne NO_PERL_CPAN_FALLBACKS_STR
+	q[@NO_PERL_CPAN_FALLBACKS@] ne NO_PERL_CPAN_FALLBACKS_STR
 );
 
 sub import {
diff --git a/perl/header_templates/fixed_prefix.template.pl b/perl/header_templates/fixed_prefix.template.pl
index 857b4391a499394ea788a6b56773aecd5afd53c3..d571ca5cde513a98cc25e7acb52b5415b98ffe10 100644
--- a/perl/header_templates/fixed_prefix.template.pl
+++ b/perl/header_templates/fixed_prefix.template.pl
@@ -1 +1 @@
-use lib (split(/@@PATHSEP@@/, $ENV{GITPERLLIB} || '@@INSTLIBDIR@@'));
+use lib (split(/@PATHSEP@/, $ENV{GITPERLLIB} || '@INSTLIBDIR@'));
diff --git a/perl/header_templates/runtime_prefix.template.pl b/perl/header_templates/runtime_prefix.template.pl
index 9d28b3d8636c6c6d48ea287a5fef0f7833f6c08c..e6f8e661a164519d4fdb55429746380e84ae3fcb 100644
--- a/perl/header_templates/runtime_prefix.template.pl
+++ b/perl/header_templates/runtime_prefix.template.pl
@@ -3,7 +3,7 @@
 # This finds our Git::* libraries relative to the script's runtime path.
 sub __git_system_path {
 	my ($relpath) = @_;
-	my $gitexecdir_relative = '@@GITEXECDIR_REL@@';
+	my $gitexecdir_relative = '@GITEXECDIR_REL@';
 
 	# GIT_EXEC_PATH is supplied by `git` or the test suite.
 	my $exec_path;
@@ -24,11 +24,11 @@ sub __git_system_path {
 }
 
 BEGIN {
-	use lib split /@@PATHSEP@@/,
+	use lib split /@PATHSEP@/,
 	(
 		$ENV{GITPERLLIB} ||
 		do {
-			my $perllibdir = __git_system_path('@@PERLLIBDIR_REL@@');
+			my $perllibdir = __git_system_path('@PERLLIBDIR_REL@');
 			(-e $perllibdir) || die("Invalid system path ($relpath): $path");
 			$perllibdir;
 		}
@@ -36,7 +36,7 @@ BEGIN
 
 	# Export the system locale directory to the I18N module. The locale directory
 	# is only installed if NO_GETTEXT is set.
-	$Git::I18N::TEXTDOMAINDIR = __git_system_path('@@LOCALEDIR_REL@@');
+	$Git::I18N::TEXTDOMAINDIR = __git_system_path('@LOCALEDIR_REL@');
 }
 
 # END RUNTIME_PREFIX generated code.
diff --git a/unimplemented.sh b/unimplemented.sh
index fee21d24e8ab89208b4aa0af7007b7c033b753bc..41776b279d4263ae6a9034b30f1bd26ac9b792bd 100644
--- a/unimplemented.sh
+++ b/unimplemented.sh
@@ -1,4 +1,4 @@
 #!/bin/sh
 
-echo >&2 "fatal: git was built without support for $(basename $0) (@@REASON@@)."
+echo >&2 "fatal: git was built without support for $(basename $0) (@REASON@)."
 exit 128
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
index 95851b85b6b7181130f0cd441c2bd7ac0bfb89da..7898a1c238dde20617cf96d144930dc59f67feab 100644
--- a/wrap-for-bin.sh
+++ b/wrap-for-bin.sh
@@ -4,33 +4,33 @@
 # to run test suite against sandbox, but with only bindir-installed
 # executables in PATH.  The Makefile copies this into various
 # files in bin-wrappers, substituting
-# @@BUILD_DIR@@ and @@PROG@@.
+# @BUILD_DIR@ and @PROG@.
 
-GIT_EXEC_PATH='@@BUILD_DIR@@'
+GIT_EXEC_PATH='@BUILD_DIR@'
 if test -n "$NO_SET_GIT_TEMPLATE_DIR"
 then
 	unset GIT_TEMPLATE_DIR
 else
-	GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
+	GIT_TEMPLATE_DIR='@BUILD_DIR@/templates/blt'
 	export GIT_TEMPLATE_DIR
 fi
-GITPERLLIB='@@BUILD_DIR@@/perl/build/lib'"${GITPERLLIB:+:$GITPERLLIB}"
-GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
-PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+GITPERLLIB='@BUILD_DIR@/perl/build/lib'"${GITPERLLIB:+:$GITPERLLIB}"
+GIT_TEXTDOMAINDIR='@BUILD_DIR@/po/build/locale'
+PATH='@BUILD_DIR@/bin-wrappers:'"$PATH"
 
 export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
 
 case "$GIT_DEBUGGER" in
 '')
-	exec "${GIT_EXEC_PATH}/@@PROG@@" "$@"
+	exec "${GIT_EXEC_PATH}/@PROG@" "$@"
 	;;
 1)
 	unset GIT_DEBUGGER
-	exec gdb --args "${GIT_EXEC_PATH}/@@PROG@@" "$@"
+	exec gdb --args "${GIT_EXEC_PATH}/@PROG@" "$@"
 	;;
 *)
 	GIT_DEBUGGER_ARGS="$GIT_DEBUGGER"
 	unset GIT_DEBUGGER
-	exec ${GIT_DEBUGGER_ARGS} "${GIT_EXEC_PATH}/@@PROG@@" "$@"
+	exec ${GIT_DEBUGGER_ARGS} "${GIT_EXEC_PATH}/@PROG@" "$@"
 	;;
 esac
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 03/22] Makefile: refactor GIT-VERSION-GEN to be reusable

Our "GIT-VERSION-GEN" script always writes the "GIT-VERSION-FILE" into the current directory, where the expectation is that it should exist in the source directory. But other build systems that support out-of-tree builds may not want to do that to keep the source directory pristine, even though CMake currently doesn't care.

Refactor the script such that it won't write the "GIT-VERSION-FILE" directly anymore, but instead knows to replace @PLACEHOLDERS@ in an arbitrary input file. This allows us to simplify the logic in CMake to determine the project version, but can also be reused later on in order to generate other files that need to contain version information like our "git.rc" file.

While at it, change the format of the version file by removing the spaces around the equals sign. Like this we can continue to include the file in our Makefiles, but can also start to source it in shell scripts in subsequent steps. Furthermore, we already start to replace some placeholders that are not yet of relevance in this commit. These are going to become relevant in subsequent steps, as well.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-VERSION-FILE.in                 |  1 +
 GIT-VERSION-GEN                     | 62 ++++++++++++++++++++++++++++---------
 Makefile                            |  5 ++-
 ci/test-documentation.sh            |  2 +-
 contrib/buildsystems/CMakeLists.txt | 23 ++++----------
 contrib/buildsystems/git-version.in |  1 +
 6 files changed, 60 insertions(+), 34 deletions(-)
Show changes to 6 files +60 −34

GIT-VERSION-FILE.in, GIT-VERSION-GEN, Makefile, ci/test-documentation.sh, contrib/buildsystems/CMakeLists.txt, contrib/buildsystems/git-version.in

diff --git a/GIT-VERSION-FILE.in b/GIT-VERSION-FILE.in
new file mode 100644
index 0000000000000000000000000000000000000000..3789a48a34a3f9e37531fc24b577ffe3c657a3e9
--- /dev/null
+++ b/GIT-VERSION-FILE.in
@@ -0,0 +1 @@
+GIT_VERSION=@GIT_VERSION@
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 78e8631f677bbebe8f18d83191f0fd014465563e..3a2dfd477b9b9c18ef34b33c9bf6a0d81b2740b5 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,23 +1,41 @@
 #!/bin/sh
 
-GVF=GIT-VERSION-FILE
 DEF_VER=v2.47.GIT
 
 LF='
 '
 
+if test "$#" -ne 3
+then
+    echo "USAGE: $0 <SOURCE_DIR> <INPUT> <OUTPUT>" >&2
+    exit 1
+fi
+
+SOURCE_DIR="$1"
+INPUT="$2"
+OUTPUT="$3"
+
+if ! test -f "$INPUT"
+then
+	echo "Input is not a file: $INPUT" >&2
+	exit 1
+fi
+
+GIT_CEILING_DIRECTORIES="$SOURCE_DIR/.."
+export GIT_CEILING_DIRECTORIES
+
 # First see if there is a version file (included in release tarballs),
 # then try git-describe, then default.
-if test -f version
+if test -f "$SOURCE_DIR"/version
 then
-	VN=$(cat version) || VN="$DEF_VER"
-elif { test -d "${GIT_DIR:-.git}" || test -f .git; } &&
-	VN=$(git describe --match "v[0-9]*" HEAD 2>/dev/null) &&
+	VN=$(cat "$SOURCE_DIR"/version) || VN="$DEF_VER"
+elif { test -d "$SOURCE_DIR/.git" || test -d "${GIT_DIR:-.git}" || test -f "$SOURCE_DIR"/.git; } &&
+	VN=$(git -C "$SOURCE_DIR" describe --match "v[0-9]*" HEAD 2>/dev/null) &&
 	case "$VN" in
 	*$LF*) (exit 1) ;;
 	v[0-9]*)
-		git update-index -q --refresh
-		test -z "$(git diff-index --name-only HEAD --)" ||
+		git -C "$SOURCE_DIR" update-index -q --refresh
+		test -z "$(git -C "$SOURCE_DIR" diff-index --name-only HEAD --)" ||
 		VN="$VN-dirty" ;;
 	esac
 then
@@ -26,15 +44,29 @@ else
 	VN="$DEF_VER"
 fi
 
-VN=$(expr "$VN" : v*'\(.*\)')
+GIT_BUILT_FROM_COMMIT=$(git -C "$SOURCE_DIR" rev-parse -q --verify HEAD 2>/dev/null)
+GIT_VERSION=$(expr "$VN" : v*'\(.*\)')
+if test -z "$GIT_USER_AGENT"
+then
+	GIT_USER_AGENT=git/$GIT_VERSION
+fi
+
+read GIT_MAJOR_VERSION GIT_MINOR_VERSION GIT_MICRO_VERSION GIT_PATCH_LEVEL trailing <<EOF
+$(echo "$GIT_VERSION" 0 0 0 0 | tr '.a-zA-Z-' ' ')
+EOF
+
+sed -e "s|@GIT_VERSION@|$GIT_VERSION|" \
+	-e "s|@GIT_MAJOR_VERSION@|$GIT_MAJOR_VERSION|" \
+	-e "s|@GIT_MINOR_VERSION@|$GIT_MINOR_VERSION|" \
+	-e "s|@GIT_MICRO_VERSION@|$GIT_MICRO_VERSION|" \
+	-e "s|@GIT_PATCH_LEVEL@|$GIT_PATCH_LEVEL|" \
+	-e "s|@GIT_USER_AGENT@|$GIT_USER_AGENT|" \
+	-e "s|@GIT_BUILT_FROM_COMMIT@|$GIT_BUILT_FROM_COMMIT|" \
+	"$INPUT" >"$OUTPUT"+
 
-if test -r $GVF
+if ! test -f "$OUTPUT" || ! cmp "$OUTPUT"+ "$OUTPUT" >/dev/null
 then
-	VC=$(sed -e 's/^GIT_VERSION = //' <$GVF)
+	mv "$OUTPUT"+ "$OUTPUT"
 else
-	VC=unset
+	rm "$OUTPUT"+
 fi
-test "$VN" = "$VC" || {
-	echo >&2 "GIT_VERSION = $VN"
-	echo "GIT_VERSION = $VN" >$GVF
-}
diff --git a/Makefile b/Makefile
index d59f6c16868e24a1e13b0c16cc05adb79f81bbc8..6c55c47e961047a8e9a80ab948e21c7466cf54de 100644
--- a/Makefile
+++ b/Makefile
@@ -592,7 +592,10 @@ include shared.mak
 #        Disable -pedantic compilation.
 
 GIT-VERSION-FILE: FORCE
-	@$(SHELL_PATH) ./GIT-VERSION-GEN
+	@OLD=$$(cat $@ 2>/dev/null || :) && \
+	$(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" GIT-VERSION-FILE.in $@ && \
+	NEW=$$(cat $@ 2>/dev/null || :) && \
+	if test "$$OLD" != "$$NEW"; then echo "$$NEW" >&2; fi
 -include GIT-VERSION-FILE
 
 # Set our default configuration.
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index 02b3af394117f840e078479ca60030141e47f998..6c018b673e0563fa5589195a77804c91deb93515 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -6,7 +6,7 @@
 . ${0%/*}/lib.sh
 
 filter_log () {
-	sed -e '/^GIT_VERSION = /d' \
+	sed -e '/^GIT_VERSION=/d' \
 	    -e "/constant Gem::ConfigMap is deprecated/d" \
 	    -e '/^    \* new asciidoc flags$/d' \
 	    -e '/stripped namespace before processing/d' \
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 8b0020edeba289e5eaa15a5452013fa2e4c3ed33..752479cac59d3833e7fff9239ebea75179692bf4 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -83,23 +83,12 @@ if(NOT SH_EXE)
 			"On Windows, you can get it as part of 'Git for Windows' install at https://gitforwindows.org/")
 endif()
 
-#Create GIT-VERSION-FILE using GIT-VERSION-GEN
-if(NOT EXISTS ${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE)
-	message("Generating GIT-VERSION-FILE")
-	execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN
-		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-endif()
-
-#Parse GIT-VERSION-FILE to get the version
-file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE git_version REGEX "GIT_VERSION = (.*)")
-string(REPLACE "GIT_VERSION = " "" git_version ${git_version})
-string(FIND ${git_version} "GIT" location)
-if(location EQUAL -1)
-	string(REGEX MATCH "[0-9]*\\.[0-9]*\\.[0-9]*" git_version ${git_version})
-else()
-	string(REGEX MATCH "[0-9]*\\.[0-9]*" git_version ${git_version})
-	string(APPEND git_version ".0") #for building from a snapshot
-endif()
+message("Generating Git version")
+execute_process(COMMAND ${SH_EXE} "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+		"${CMAKE_SOURCE_DIR}"
+		"${CMAKE_SOURCE_DIR}/contrib/buildsystems/git-version.in"
+		"${CMAKE_BINARY_DIR}/git-version")
+file(STRINGS "${CMAKE_BINARY_DIR}/git-version" git_version)
 
 project(git
 	VERSION ${git_version}
diff --git a/contrib/buildsystems/git-version.in b/contrib/buildsystems/git-version.in
new file mode 100644
index 0000000000000000000000000000000000000000..9750505ae77685ebb31a38468caaf13501b6739d
--- /dev/null
+++ b/contrib/buildsystems/git-version.in
@@ -0,0 +1 @@
+@GIT_MAJOR_VERSION@.@GIT_MINOR_VERSION@.@GIT_MICRO_VERSION@
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 04/22] Makefile: propagate Git version via generated header

We set up a couple of preprocessor macros when compiling Git that propagate the version that Git was built from to `git version` et al. The way this is set up makes it harder than necessary to reuse the infrastructure across the different build systems.

Refactor this such that we generate a "version-def.h" header via `GIT-VERSION-GEN` instead.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 .gitignore                          |  1 +
 Makefile                            | 12 +++++-------
 contrib/buildsystems/CMakeLists.txt | 16 ++++++++++++----
 version-def.h.in                    |  8 ++++++++
 version.c                           |  1 +
 5 files changed, 27 insertions(+), 11 deletions(-)
Show changes to 5 files +27 −11

.gitignore, Makefile, contrib/buildsystems/CMakeLists.txt, version-def.h.in, version.c

diff --git a/.gitignore b/.gitignore
index 6687bd6db4c0a6adf02292a9cb545d3e5270e2b0..e17963e84252fa094c10f5e84c0e3724153c819c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -195,6 +195,7 @@
 /config-list.h
 /command-list.h
 /hook-list.h
+/version-def.h
 *.tar.gz
 *.dsc
 *.deb
diff --git a/Makefile b/Makefile
index 6c55c47e961047a8e9a80ab948e21c7466cf54de..28807f6586a69d6ce0abf1b20ce31ca58e9d02a9 100644
--- a/Makefile
+++ b/Makefile
@@ -2508,13 +2508,11 @@ PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
 pager.sp pager.s pager.o: EXTRA_CPPFLAGS = \
 	-DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
 
-version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
-version.sp version.s version.o: EXTRA_CPPFLAGS = \
-	'-DGIT_VERSION="$(GIT_VERSION)"' \
-	'-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)' \
-	'-DGIT_BUILT_FROM_COMMIT="$(shell \
-		GIT_CEILING_DIRECTORIES="$(CURDIR)/.." \
-		git rev-parse -q --verify HEAD 2>/dev/null)"'
+version-def.h: version-def.h.in GIT-VERSION-GEN GIT-VERSION-FILE GIT-USER-AGENT
+	$(QUIET_GEN)GIT_USER_AGENT="$(GIT_USER_AGENT)" $(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
+	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+
+version.sp version.s version.o: version-def.h
 
 $(BUILT_INS): git$X
 	$(QUIET_BUILT_IN)$(RM) $@ && \
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 752479cac59d3833e7fff9239ebea75179692bf4..b9ebe2ef74e37d5fad51f6e23aa234c563cad392 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -229,10 +229,7 @@ add_compile_definitions(PAGER_ENV="LESS=FRX LV=-c"
 			GIT_HTML_PATH="share/doc/git-doc"
 			DEFAULT_HELP_FORMAT="html"
 			DEFAULT_GIT_TEMPLATE_DIR="share/git-core/templates"
-			GIT_VERSION="${PROJECT_VERSION}.GIT"
-			GIT_USER_AGENT="git/${PROJECT_VERSION}.GIT"
-			BINDIR="bin"
-			GIT_BUILT_FROM_COMMIT="")
+			BINDIR="bin")
 
 if(WIN32)
 	set(FALLBACK_RUNTIME_PREFIX /mingw64)
@@ -668,6 +665,17 @@ parse_makefile_for_sources(libgit_SOURCES "LIB_OBJS")
 
 list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+
+add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/version-def.h"
+	COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+		"${CMAKE_SOURCE_DIR}"
+		"${CMAKE_SOURCE_DIR}/version-def.h.in"
+		"${CMAKE_BINARY_DIR}/version-def.h"
+	DEPENDS "${SH_EXE}" "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+		"${CMAKE_SOURCE_DIR}/version-def.h.in"
+	VERBATIM)
+list(APPEND libgit_SOURCES "${CMAKE_BINARY_DIR}/version-def.h")
+
 add_library(libgit ${libgit_SOURCES} ${compat_SOURCES})
 
 #libxdiff
diff --git a/version-def.h.in b/version-def.h.in
new file mode 100644
index 0000000000000000000000000000000000000000..347995df0611e19428cf96ec9539f0dcfea999d2
--- /dev/null
+++ b/version-def.h.in
@@ -0,0 +1,8 @@
+#ifndef VERSION_DEF_H
+#define VERSION_DEF_H
+
+#define GIT_VERSION "@GIT_VERSION@"
+#define GIT_BUILT_FROM_COMMIT "@GIT_BUILT_FROM_COMMIT@"
+#define GIT_USER_AGENT "@GIT_USER_AGENT@"
+
+#endif /* VERSION_DEF_H */
diff --git a/version.c b/version.c
index 41b718c29e1b9fc2981d7d14a3d25e69c31a3030..7adc4d51ff25a0841607c21807deaf20721252de 100644
--- a/version.c
+++ b/version.c
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "version.h"
+#include "version-def.h"
 #include "strbuf.h"
 
 const char git_version_string[] = GIT_VERSION;
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 05/22] Makefile: generate "git.rc" via GIT-VERSION-GEN

The "git.rc" is used on Windows to embed information like the project name and version into the resulting executables. As such we need to inject the version information, which we do by using preprocessor defines. The logic to do so is non-trivial and needs to be kept in sync with the different build systems.

Refactor the logic so that we generate "git.rc" via `GIT-VERSION-GEN`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 .gitignore                          |  1 +
 Makefile                            | 11 ++++++-----
 contrib/buildsystems/CMakeLists.txt | 19 +++++++++++++------
 git.rc => git.rc.in                 |  6 +++---
 4 files changed, 23 insertions(+), 14 deletions(-)
Show changes to 5 files +23 −14

.gitignore, Makefile, contrib/buildsystems/CMakeLists.txt, git.rc, git.rc.in

diff --git a/.gitignore b/.gitignore
index e17963e84252fa094c10f5e84c0e3724153c819c..d3be460040c6120d862a25dff1abd325b93b37fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -199,6 +199,7 @@
 *.tar.gz
 *.dsc
 *.deb
+/git.rc
 /git.spec
 *.exe
 *.[aos]
diff --git a/Makefile b/Makefile
index 28807f6586a69d6ce0abf1b20ce31ca58e9d02a9..eced752985caa10a21870c4ba638b4e6fbb3e5ec 100644
--- a/Makefile
+++ b/Makefile
@@ -2568,11 +2568,12 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
 	$(QUIET_GEN)$(cmd_munge_script) && \
 	mv $@+ $@
 
-git.res: git.rc GIT-VERSION-FILE GIT-PREFIX
-	$(QUIET_RC)$(RC) \
-	  $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
-	    $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
-	  -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
+git.rc: git.rc.in GIT-VERSION-GEN GIT-VERSION-FILE
+	$(QUIET_GEN)$(SHELL_PATH) ./GIT-VERSION-GEN "$(shell pwd)" $< $@+
+	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+
+git.res: git.rc GIT-PREFIX
+	$(QUIET_RC)$(RC) -i $< -o $@
 
 # This makes sure we depend on the NO_PERL setting itself.
 $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index b9ebe2ef74e37d5fad51f6e23aa234c563cad392..b893b0143a72ad284887d1d4ca72f1beb0a39ad7 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -691,18 +691,25 @@ list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 add_library(reftable STATIC ${reftable_SOURCES})
 
 if(WIN32)
+	add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.rc
+			COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+				"${CMAKE_SOURCE_DIR}"
+				"${CMAKE_SOURCE_DIR}/git.rc.in"
+				"${CMAKE_BINARY_DIR}/git.rc"
+			DEPENDS "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+				"${CMAKE_SOURCE_DIR}/git.rc.in"
+			VERBATIM)
+
 	if(NOT MSVC)#use windres when compiling with gcc and clang
 		add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
-				COMMAND ${WINDRES_EXE} -O coff -DMAJOR=${PROJECT_VERSION_MAJOR} -DMINOR=${PROJECT_VERSION_MINOR}
-					-DMICRO=${PROJECT_VERSION_PATCH} -DPATCHLEVEL=0 -DGIT_VERSION="\\\"${PROJECT_VERSION}.GIT\\\""
-					-i ${CMAKE_SOURCE_DIR}/git.rc -o ${CMAKE_BINARY_DIR}/git.res
+				COMMAND ${WINDRES_EXE} -O coff -i ${CMAKE_BINARY_DIR}/git.rc -o ${CMAKE_BINARY_DIR}/git.res
+				DEPENDS "${CMAKE_BINARY_DIR}/git.rc"
 				WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 				VERBATIM)
 	else()#MSVC use rc
 		add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
-				COMMAND ${CMAKE_RC_COMPILER} /d MAJOR=${PROJECT_VERSION_MAJOR} /d MINOR=${PROJECT_VERSION_MINOR}
-					/d MICRO=${PROJECT_VERSION_PATCH} /d PATCHLEVEL=0 /d GIT_VERSION="${PROJECT_VERSION}.GIT"
-					/fo ${CMAKE_BINARY_DIR}/git.res ${CMAKE_SOURCE_DIR}/git.rc
+				COMMAND ${CMAKE_RC_COMPILER} /fo ${CMAKE_BINARY_DIR}/git.res ${CMAKE_BINARY_DIR}/git.rc
+				DEPENDS "${CMAKE_BINARY_DIR}/git.rc"
 				WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 				VERBATIM)
 	endif()
diff --git a/git.rc b/git.rc.in
similarity index 67%
rename from git.rc
rename to git.rc.in
index cc3fdc6cc6cb83b084eebe2ad49f3c78c981789b..e69444eef3f0c558707e067d52820668d2116308 100644
--- a/git.rc
+++ b/git.rc.in
@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION     MAJOR,MINOR,MICRO,PATCHLEVEL
-PRODUCTVERSION  MAJOR,MINOR,MICRO,PATCHLEVEL
+FILEVERSION     @GIT_MAJOR_VERSION@,@GIT_MINOR_VERSION@,@GIT_MICRO_VERSION@,@GIT_PATCH_LEVEL@
+PRODUCTVERSION  @GIT_MAJOR_VERSION@,@GIT_MINOR_VERSION@,@GIT_MICRO_VERSION@,@GIT_PATCH_LEVEL@
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
@@ -11,7 +11,7 @@ BEGIN
       VALUE "InternalName", "git\0"
       VALUE "OriginalFilename", "git.exe\0"
       VALUE "ProductName", "Git\0"
-      VALUE "ProductVersion", GIT_VERSION "\0"
+      VALUE "ProductVersion", "@GIT_VERSION@\0"
     END
   END
 
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 06/22] Makefile: consistently use PERL_PATH

When injecting the Perl path into our scripts we sometimes use '@PERL@' while we othertimes use '@PERL_PATH@'. Refactor the code use the latter consistently, which makes it easier to reuse the same logic for multiple scripts.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Makefile                            | 2 +-
 contrib/buildsystems/CMakeLists.txt | 2 +-
 git-instaweb.sh                     | 4 ++--
 git-request-pull.sh                 | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)
Show changes to 4 files +5 −5

Makefile, contrib/buildsystems/CMakeLists.txt, git-instaweb.sh, git-request-pull.sh

diff --git a/Makefile b/Makefile
index eced752985caa10a21870c4ba638b4e6fbb3e5ec..fd4b061d0ad140cf0c9fcc09aa5946b2ca0ca7a5 100644
--- a/Makefile
+++ b/Makefile
@@ -2554,7 +2554,7 @@ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's/@USE_GETTEXT_SCHEME@/$(USE_GETTEXT_SCHEME)/g' \
     -e $(BROKEN_PATH_FIX) \
     -e 's|@GITWEBDIR@|$(gitwebdir_SQ)|g' \
-    -e 's|@PERL@|$(PERL_PATH_SQ)|g' \
+    -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' \
     -e 's|@PAGER_ENV@|$(PAGER_ENV_SQ)|g' \
     $@.sh >$@+
 endef
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index b893b0143a72ad284887d1d4ca72f1beb0a39ad7..f8892c49c6d317040b3b6447b88133b2bfff3fcf 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -846,7 +846,7 @@ foreach(script ${git_shell_scripts})
 	string(REPLACE "@NO_CURL@" "" content "${content}")
 	string(REPLACE "@USE_GETTEXT_SCHEME@" "" content "${content}")
 	string(REPLACE "# @BROKEN_PATH_FIX@" "" content "${content}")
-	string(REPLACE "@PERL@" "${PERL_PATH}" content "${content}")
+	string(REPLACE "@PERL_PATH@" "${PERL_PATH}" content "${content}")
 	string(REPLACE "@PAGER_ENV@" "LESS=FRX LV=-c" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
 endforeach()
diff --git a/git-instaweb.sh b/git-instaweb.sh
index c8efb1205a8e2a66a6aced840980978897e122bc..5ad50160bb035885d8c180bee3ebb0a8e9622abc 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-PERL='@PERL@'
+PERL='@PERL_PATH@'
 OPTIONS_KEEPDASHDASH=
 OPTIONS_STUCKLONG=
 OPTIONS_SPEC="\
@@ -716,7 +716,7 @@ EOF
 
 gitweb_conf() {
 	cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
-#!@PERL@
+#!@PERL_PATH@
 our \$projectroot = "$(dirname "$fqgitdir")";
 our \$git_temp = "$fqgitdir/gitweb/tmp";
 our \$projects_list = \$projectroot;
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 10c88639e28c02650ffea3d4671afa9a9f0bb182..775ba8ea11aa0fce90ae9bfaea8eac5b8bc584a4 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -112,7 +112,7 @@ find_matching_ref='
 	}
 '
 
-set fnord $(git ls-remote "$url" | @PERL@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
+set fnord $(git ls-remote "$url" | @PERL_PATH@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")
 remote_sha1=$2
 ref=$3
 
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 07/22] Makefile: extract script to massage Perl scripts

Extract the script to inject various build-time parameters into our Perl scripts into a standalone script. This is done such that we can reuse it in other build systems.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Makefile                            | 12 ++----------
 contrib/buildsystems/CMakeLists.txt | 32 +++++++++++++++++++++++++++-----
 generate-perl.sh                    | 27 +++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 15 deletions(-)
Show changes to 3 files +56 −15

Makefile, contrib/buildsystems/CMakeLists.txt, generate-perl.sh

diff --git a/Makefile b/Makefile
index fd4b061d0ad140cf0c9fcc09aa5946b2ca0ca7a5..669ee4fb8d28fd9e1aeb0a794d5ce44ba60b5c9c 100644
--- a/Makefile
+++ b/Makefile
@@ -2606,16 +2606,8 @@ endif
 
 PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir)
 
-$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
-	$(QUIET_GEN) \
-	sed -e '1{' \
-	    -e '	s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-	    -e '	r GIT-PERL-HEADER' \
-	    -e '	G' \
-	    -e '}' \
-	    -e 's/@GIT_VERSION@/$(GIT_VERSION)/g' \
-	    $< >$@+ && \
-	chmod +x $@+ && \
+$(SCRIPT_PERL_GEN): % : %.perl generate-perl.sh GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
+	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@+" && \
 	mv $@+ $@
 
 PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES))
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index f8892c49c6d317040b3b6447b88133b2bfff3fcf..e2e7d3da2028cd36a9761f78607539080361b3ec 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -852,19 +852,41 @@ foreach(script ${git_shell_scripts})
 endforeach()
 
 #perl scripts
-parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" ".perl")
+parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" "")
 
 #create perl header
 file(STRINGS ${CMAKE_SOURCE_DIR}/perl/header_templates/fixed_prefix.template.pl perl_header )
 string(REPLACE "@PATHSEP@" ":" perl_header "${perl_header}")
 string(REPLACE "@INSTLIBDIR@" "${INSTLIBDIR}" perl_header "${perl_header}")
+file(WRITE ${CMAKE_BINARY_DIR}/PERL-HEADER ${perl_header})
+
+add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
+	COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+		"${CMAKE_SOURCE_DIR}"
+		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
+		"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
+	DEPENDS ${SH_EXE} "${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN"
+		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
+	VERBATIM)
 
 foreach(script ${git_perl_scripts})
-	file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.perl content NEWLINE_CONSUME)
-	string(REPLACE "#!/usr/bin/perl" "#!/usr/bin/perl\n${perl_header}\n" content "${content}")
-	string(REPLACE "@GIT_VERSION@" "${PROJECT_VERSION}" content "${content}")
-	file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
+	string(REPLACE ".perl" "" perl_gen_path "${script}")
+
+	add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${perl_gen_path}"
+		COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-perl.sh"
+			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
+			"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
+			"${CMAKE_BINARY_DIR}/PERL-HEADER"
+			"${CMAKE_SOURCE_DIR}/${script}"
+			"${CMAKE_BINARY_DIR}/${perl_gen_path}"
+		DEPENDS "${CMAKE_SOURCE_DIR}/generate-perl.sh"
+			"${CMAKE_SOURCE_DIR}/${script}"
+			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
+			"${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
+		VERBATIM)
+	list(APPEND perl_gen ${CMAKE_BINARY_DIR}/${perl_gen_path})
 endforeach()
+add_custom_target(perl-gen ALL DEPENDS ${perl_gen})
 
 #python script
 file(STRINGS ${CMAKE_SOURCE_DIR}/git-p4.py content NEWLINE_CONSUME)
diff --git a/generate-perl.sh b/generate-perl.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f168cce734dbbe80b58b4b761b13e8a177f70642
--- /dev/null
+++ b/generate-perl.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+set -e
+
+if test $# -ne 5
+then
+	echo "USAGE: $0 <GIT_BUILD_OPTIONS> <GIT_VERSION_FILE> <PERL_HEADER> <INPUT> <OUTPUT>" >&2
+	exit 1
+fi
+
+GIT_BUILD_OPTIONS="$1"
+GIT_VERSION_FILE="$2"
+PERL_HEADER="$3"
+INPUT="$4"
+OUTPUT="$5"
+
+. "$GIT_BUILD_OPTIONS"
+. "$GIT_VERSION_FILE"
+
+sed -e '1{' \
+    -e "	s|#!.*perl|#!$PERL_PATH|" \
+    -e "	r $PERL_HEADER" \
+    -e '	G' \
+    -e '}' \
+    -e "s/@GIT_VERSION@/$GIT_VERSION/g" \
+    "$INPUT" >"$OUTPUT"
+chmod a+x "$OUTPUT"
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 08/22] Makefile: use "generate-perl.sh" to massage Perl library

Extend "generate-perl.sh" such that it knows to also massage the Perl library files. There are two major differences:

  - We do not read in the Perl header. This is handled by matching on
    whether or not we have a Perl shebang.
  - We substitute some more variables, which we read in via our
    GIT-BUILD-OPTIONS.
Adapt both our Makefile and the CMake build instructions to use this.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-BUILD-OPTIONS.in                |  2 ++
 Makefile                            | 10 ++++------
 contrib/buildsystems/CMakeLists.txt | 23 +++++++++--------------
 generate-perl.sh                    | 14 ++++++++++++--
 4 files changed, 27 insertions(+), 22 deletions(-)
Show changes to 4 files +27 −22

GIT-BUILD-OPTIONS.in, Makefile, contrib/buildsystems/CMakeLists.txt, generate-perl.sh

diff --git a/GIT-BUILD-OPTIONS.in b/GIT-BUILD-OPTIONS.in
index f0ca240493c94aa41a6b6241a8474e42f7cdc8b9..050432f9fc49f93d0f6ed98c1307405c52761be0 100644
--- a/GIT-BUILD-OPTIONS.in
+++ b/GIT-BUILD-OPTIONS.in
@@ -1,6 +1,8 @@
 SHELL_PATH=@SHELL_PATH@
 TEST_SHELL_PATH=@TEST_SHELL_PATH@
 PERL_PATH=@PERL_PATH@
+PERL_LOCALEDIR=@PERL_LOCALEDIR@
+NO_PERL_CPAN_FALLBACKS=@NO_PERL_CPAN_FALLBACKS@
 DIFF=@DIFF@
 PYTHON_PATH=@PYTHON_PATH@
 TAR=@TAR@
diff --git a/Makefile b/Makefile
index 669ee4fb8d28fd9e1aeb0a794d5ce44ba60b5c9c..8aaa5bbfe2bd11aa88b7578e17b9839b1b9b0a0b 100644
--- a/Makefile
+++ b/Makefile
@@ -3095,13 +3095,9 @@ endif
 NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
 endif
 
-perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
+perl/build/lib/%.pm: perl/%.pm generate-perl.sh GIT-BUILD-OPTIONS GIT-VERSION-FILE GIT-PERL-DEFINES
 	$(call mkdir_p_parent_template)
-	$(QUIET_GEN) \
-	sed -e 's|@LOCALEDIR@|$(perl_localedir_SQ)|g' \
-	    -e 's|@NO_GETTEXT@|$(NO_GETTEXT_SQ)|g' \
-	    -e 's|@NO_PERL_CPAN_FALLBACKS@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
-	< $< > $@
+	$(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@"
 
 perl/build/man/man3/Git.3pm: perl/Git.pm
 	$(call mkdir_p_parent_template)
@@ -3162,6 +3158,8 @@ GIT-BUILD-OPTIONS: FORCE
 		-e "s|@SHELL_PATH@|\'$(SHELL_PATH_SQ)\'|" \
 		-e "s|@TEST_SHELL_PATH@|\'$(TEST_SHELL_PATH_SQ)\'|" \
 		-e "s|@PERL_PATH@|\'$(PERL_PATH_SQ)\'|" \
+		-e "s|@PERL_LOCALEDIR@|\'$(perl_localedir_SQ)\'|" \
+		-e "s|@NO_PERL_CPAN_FALLBACKS@|\'$(NO_PERL_CPAN_FALLBACKS_SQ)\'|" \
 		-e "s|@DIFF@|\'$(DIFF)\'|" \
 		-e "s|@PYTHON_PATH@|\'$(PYTHON_PATH_SQ)\'|" \
 		-e "s|@TAR@|\'$(TAR)\'|" \
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index e2e7d3da2028cd36a9761f78607539080361b3ec..0f9180937e76cad4ad831d34877ea276413e76c9 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -853,6 +853,9 @@ endforeach()
 
 #perl scripts
 parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" "")
+#perl modules
+file(GLOB_RECURSE perl_modules "${CMAKE_SOURCE_DIR}/perl/*.pm")
+list(TRANSFORM perl_modules REPLACE "${CMAKE_SOURCE_DIR}/" "")
 
 #create perl header
 file(STRINGS ${CMAKE_SOURCE_DIR}/perl/header_templates/fixed_prefix.template.pl perl_header )
@@ -869,9 +872,12 @@ add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE"
 		"${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE.in"
 	VERBATIM)
 
-foreach(script ${git_perl_scripts})
+foreach(script ${git_perl_scripts} ${perl_modules})
 	string(REPLACE ".perl" "" perl_gen_path "${script}")
 
+	get_filename_component(perl_gen_dir "${perl_gen_path}" DIRECTORY)
+	file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/${perl_gen_dir}")
+
 	add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${perl_gen_path}"
 		COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-perl.sh"
 			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
@@ -893,19 +899,6 @@ file(STRINGS ${CMAKE_SOURCE_DIR}/git-p4.py content NEWLINE_CONSUME)
 string(REPLACE "#!/usr/bin/env python" "#!/usr/bin/python" content "${content}")
 file(WRITE ${CMAKE_BINARY_DIR}/git-p4 ${content})
 
-#perl modules
-file(GLOB_RECURSE perl_modules "${CMAKE_SOURCE_DIR}/perl/*.pm")
-
-foreach(pm ${perl_modules})
-	string(REPLACE "${CMAKE_SOURCE_DIR}/perl/" "" file_path ${pm})
-	file(STRINGS ${pm} content NEWLINE_CONSUME)
-	string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" content "${content}")
-	string(REPLACE "@NO_PERL_CPAN_FALLBACKS@" "" content "${content}")
-	file(WRITE ${CMAKE_BINARY_DIR}/perl/build/lib/${file_path} ${content})
-#test-lib.sh requires perl/build/lib to be the build directory of perl modules
-endforeach()
-
-
 #templates
 file(GLOB templates "${CMAKE_SOURCE_DIR}/templates/*")
 list(TRANSFORM templates REPLACE "${CMAKE_SOURCE_DIR}/templates/" "")
@@ -1147,6 +1140,8 @@ file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_
 string(REPLACE "@SHELL_PATH@" "'${SHELL_PATH}'" git_build_options "${git_build_options}")
 string(REPLACE "@TEST_SHELL_PATH@" "'${TEST_SHELL_PATH}'" git_build_options "${git_build_options}")
 string(REPLACE "@PERL_PATH@" "'${PERL_PATH}'" git_build_options "${git_build_options}")
+string(REPLACE "@PERL_LOCALEDIR@" "'${LOCALEDIR}'" git_build_options "${git_build_options}")
+string(REPLACE "@NO_PERL_CPAN_FALLBACKS@" "" git_build_options "${git_build_options}")
 string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
 string(REPLACE "@PYTHON_PATH@" "'${PYTHON_PATH}'" git_build_options "${git_build_options}")
 string(REPLACE "@TAR@" "'${TAR}'" git_build_options "${git_build_options}")
diff --git a/generate-perl.sh b/generate-perl.sh
index f168cce734dbbe80b58b4b761b13e8a177f70642..d9eaed86218c5f74fdae06bf3170808104058fa7 100755
--- a/generate-perl.sh
+++ b/generate-perl.sh
@@ -18,10 +18,20 @@ OUTPUT="$5"
 . "$GIT_VERSION_FILE"
 
 sed -e '1{' \
+    -e "	/^#!.*perl/!b" \
     -e "	s|#!.*perl|#!$PERL_PATH|" \
     -e "	r $PERL_HEADER" \
     -e '	G' \
     -e '}' \
-    -e "s/@GIT_VERSION@/$GIT_VERSION/g" \
+    -e "s|@GIT_VERSION@|$GIT_VERSION|g" \
+    -e "s|@LOCALEDIR@|$PERL_LOCALEDIR|g" \
+    -e "s|@NO_GETTEXT@|$NO_GETTEXT|g" \
+    -e "s|@NO_PERL_CPAN_FALLBACKS@|$NO_PERL_CPAN_FALLBACKS|g" \
     "$INPUT" >"$OUTPUT"
-chmod a+x "$OUTPUT"
+
+case "$INPUT" in
+*.perl)
+	chmod a+x "$OUTPUT";;
+*)
+	;;
+esac
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 10/22] Makefile: extract script to generate gitweb.cgi

In order to generate "gitweb.cgi" we have to replace various different placeholders. This is done ad-hoc and is thus not easily reusable across different build systems.

Introduce a new GITWEB-BUILD-OPTIONS.in template that we populate at configuration time with the expected options. This script is then used as input for a new "generate-gitweb.sh" script that generates the final "gitweb.cgi" file. While this requires us to repeat the options multiple times, it is in line to how we generate other build options like our GIT-BUILD-OPTIONS file.

While at it, refactor how we replace the GITWEB_PROJECT_MAXDEPTH. Even though this variable is supposed to be an integer, the source file has the value quoted. The quotes are eventually stripped via sed(1), which replaces `"@GITWEB_PROJECT_MAXDEPTH@"` with the actual value, which is rather nonsensical. This is made clearer by just dropping the quotes in the source file.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 gitweb/GITWEB-BUILD-OPTIONS.in | 24 +++++++++++++++++
 gitweb/Makefile                | 59 +++++++++++++++++++++---------------------
 gitweb/generate-gitweb-cgi.sh  | 47 +++++++++++++++++++++++++++++++++
 gitweb/gitweb.perl             |  2 +-
 4 files changed, 101 insertions(+), 31 deletions(-)
Show changes to 4 files +101 −31

gitweb/GITWEB-BUILD-OPTIONS.in, gitweb/Makefile, gitweb/generate-gitweb-cgi.sh, gitweb/gitweb.perl

diff --git a/gitweb/GITWEB-BUILD-OPTIONS.in b/gitweb/GITWEB-BUILD-OPTIONS.in
new file mode 100644
index 0000000000000000000000000000000000000000..41ac20654c4f79917993bafa8f7e447669552b9d
--- /dev/null
+++ b/gitweb/GITWEB-BUILD-OPTIONS.in
@@ -0,0 +1,24 @@
+PERL_PATH=@PERL_PATH@
+JSMIN=@JSMIN@
+CSSMIN=@CSSMIN@
+GIT_BINDIR=@GIT_BINDIR@
+GITWEB_CONFIG=@GITWEB_CONFIG@
+GITWEB_CONFIG_SYSTEM=@GITWEB_CONFIG_SYSTEM@
+GITWEB_CONFIG_COMMON=@GITWEB_CONFIG_COMMON@
+GITWEB_HOME_LINK_STR=@GITWEB_HOME_LINK_STR@
+GITWEB_SITENAME=@GITWEB_SITENAME@
+GITWEB_PROJECTROOT=@GITWEB_PROJECTROOT@
+GITWEB_PROJECT_MAXDEPTH=@GITWEB_PROJECT_MAXDEPTH@
+GITWEB_EXPORT_OK=@GITWEB_EXPORT_OK@
+GITWEB_STRICT_EXPORT=@GITWEB_STRICT_EXPORT@
+GITWEB_BASE_URL=@GITWEB_BASE_URL@
+GITWEB_LIST=@GITWEB_LIST@
+GITWEB_HOMETEXT=@GITWEB_HOMETEXT@
+GITWEB_CSS=@GITWEB_CSS@
+GITWEB_LOGO=@GITWEB_LOGO@
+GITWEB_FAVICON=@GITWEB_FAVICON@
+GITWEB_JS=@GITWEB_JS@
+GITWEB_SITE_HTML_HEAD_STRING=@GITWEB_SITE_HTML_HEAD_STRING@
+GITWEB_SITE_HEADER=@GITWEB_SITE_HEADER@
+GITWEB_SITE_FOOTER=@GITWEB_SITE_FOOTER@
+HIGHLIGHT_BIN=@HIGHLIGHT_BIN@
diff --git a/gitweb/Makefile b/gitweb/Makefile
index 164c8d53757f98599ad14eeb22ca8d542eb7502a..16a2ef2d1e5664d99f6f1d8ff4224c36769c55fb 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -77,43 +77,42 @@ GITWEB_JSLIB_FILES += static/js/javascript-detection.js
 GITWEB_JSLIB_FILES += static/js/adjust-timezone.js
 GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
-
-GITWEB_REPLACE = \
-	-e 's|@GIT_VERSION@|$(GIT_VERSION)|g' \
-	-e 's|@GIT_BINDIR@|$(bindir)|g' \
-	-e 's|@GITWEB_CONFIG@|$(GITWEB_CONFIG)|g' \
-	-e 's|@GITWEB_CONFIG_SYSTEM@|$(GITWEB_CONFIG_SYSTEM)|g' \
-	-e 's|@GITWEB_CONFIG_COMMON@|$(GITWEB_CONFIG_COMMON)|g' \
-	-e 's|@GITWEB_HOME_LINK_STR@|$(GITWEB_HOME_LINK_STR)|g' \
-	-e 's|@GITWEB_SITENAME@|$(GITWEB_SITENAME)|g' \
-	-e 's|@GITWEB_PROJECTROOT@|$(GITWEB_PROJECTROOT)|g' \
-	-e 's|"@GITWEB_PROJECT_MAXDEPTH@"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
-	-e 's|@GITWEB_EXPORT_OK@|$(GITWEB_EXPORT_OK)|g' \
-	-e 's|@GITWEB_STRICT_EXPORT@|$(GITWEB_STRICT_EXPORT)|g' \
-	-e 's|@GITWEB_BASE_URL@|$(GITWEB_BASE_URL)|g' \
-	-e 's|@GITWEB_LIST@|$(GITWEB_LIST)|g' \
-	-e 's|@GITWEB_HOMETEXT@|$(GITWEB_HOMETEXT)|g' \
-	-e 's|@GITWEB_CSS@|$(GITWEB_CSS)|g' \
-	-e 's|@GITWEB_LOGO@|$(GITWEB_LOGO)|g' \
-	-e 's|@GITWEB_FAVICON@|$(GITWEB_FAVICON)|g' \
-	-e 's|@GITWEB_JS@|$(GITWEB_JS)|g' \
-	-e 's|@GITWEB_SITE_HTML_HEAD_STRING@|$(GITWEB_SITE_HTML_HEAD_STRING)|g' \
-	-e 's|@GITWEB_SITE_HEADER@|$(GITWEB_SITE_HEADER)|g' \
-	-e 's|@GITWEB_SITE_FOOTER@|$(GITWEB_SITE_FOOTER)|g' \
-	-e 's|@HIGHLIGHT_BIN@|$(HIGHLIGHT_BIN)|g'
-
 .PHONY: FORCE
 $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS: FORCE
-	@rm -f $@+
-	@echo "x" '$(PERL_PATH_SQ)' $(GITWEB_REPLACE) "$(JSMIN)|$(CSSMIN)" >$@+
+	@sed -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|' \
+	     -e 's|@JSMIN@|$(JSMIN)|' \
+	     -e 's|@CSSMIN@|$(CSSMIN)|' \
+	     -e 's|@GIT_VERSION@|$(GIT_VERSION)|' \
+	     -e 's|@GIT_BINDIR@|$(bindir)|' \
+	     -e 's|@GITWEB_CONFIG@|$(GITWEB_CONFIG)|' \
+	     -e 's|@GITWEB_CONFIG_SYSTEM@|$(GITWEB_CONFIG_SYSTEM)|' \
+	     -e 's|@GITWEB_CONFIG_COMMON@|$(GITWEB_CONFIG_COMMON)|' \
+	     -e 's|@GITWEB_HOME_LINK_STR@|$(GITWEB_HOME_LINK_STR)|' \
+	     -e 's|@GITWEB_SITENAME@|$(GITWEB_SITENAME)|' \
+	     -e 's|@GITWEB_PROJECTROOT@|$(GITWEB_PROJECTROOT)|' \
+	     -e 's|@GITWEB_PROJECT_MAXDEPTH@|$(GITWEB_PROJECT_MAXDEPTH)|' \
+	     -e 's|@GITWEB_EXPORT_OK@|$(GITWEB_EXPORT_OK)|' \
+	     -e 's|@GITWEB_STRICT_EXPORT@|$(GITWEB_STRICT_EXPORT)|' \
+	     -e 's|@GITWEB_BASE_URL@|$(GITWEB_BASE_URL)|' \
+	     -e 's|@GITWEB_LIST@|$(GITWEB_LIST)|' \
+	     -e 's|@GITWEB_HOMETEXT@|$(GITWEB_HOMETEXT)|' \
+	     -e 's|@GITWEB_CSS@|$(GITWEB_CSS)|' \
+	     -e 's|@GITWEB_LOGO@|$(GITWEB_LOGO)|' \
+	     -e 's|@GITWEB_FAVICON@|$(GITWEB_FAVICON)|' \
+	     -e 's|@GITWEB_JS@|$(GITWEB_JS)|' \
+	     -e 's|@GITWEB_SITE_HTML_HEAD_STRING@|$(GITWEB_SITE_HTML_HEAD_STRING)|' \
+	     -e 's|@GITWEB_SITE_HEADER@|$(GITWEB_SITE_HEADER)|' \
+	     -e 's|@GITWEB_SITE_FOOTER@|$(GITWEB_SITE_FOOTER)|' \
+	     -e 's|@HIGHLIGHT_BIN@|$(HIGHLIGHT_BIN)|' \
+	     $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS.in >"$@+"
 	@cmp -s $@+ $@ && rm -f $@+ || mv -f $@+ $@
 
+$(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)generate-gitweb-cgi.sh
 $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)GITWEB-BUILD-OPTIONS
+$(MAK_DIR_GITWEB)gitweb.cgi: GIT-VERSION-FILE
 $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)gitweb.perl
 	$(QUIET_GEN)$(RM) $@ $@+ && \
-	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-		$(GITWEB_REPLACE) $< >$@+ && \
-	chmod +x $@+ && \
+	$(MAK_DIR_GITWEB)generate-gitweb-cgi.sh $(MAK_DIR_GITWEB)/GITWEB-BUILD-OPTIONS ./GIT-VERSION-FILE $< $@+ && \
 	mv $@+ $@
 
 $(MAK_DIR_GITWEB)static/gitweb.js: $(addprefix $(MAK_DIR_GITWEB),$(GITWEB_JSLIB_FILES))
diff --git a/gitweb/generate-gitweb-cgi.sh b/gitweb/generate-gitweb-cgi.sh
new file mode 100755
index 0000000000000000000000000000000000000000..67a646aeb7c22fd69817ed14c9937f3123736405
--- /dev/null
+++ b/gitweb/generate-gitweb-cgi.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+set -e
+
+if test $# -ne 4
+then
+	echo "USAGE: $0 <GITWEB-BUILD-OPTIONS> <GIT-VERSION-FILE> <INPUT> <OUTPUT>" >&2
+	exit 1
+fi
+
+GITWEB_BUILD_OPTIONS="$1"
+GIT_VERSION_FILE="$2"
+INPUT="$3"
+OUTPUT="$4"
+
+. "$GITWEB_BUILD_OPTIONS"
+. "$GIT_VERSION_FILE"
+
+sed -e "1s|#!/usr/bin/perl|#!$PERL_PATH|" \
+    -e "s|@PERL_PATH@|$PERL_PATH|" \
+    -e "s|@JSMIN@|$JSMIN|" \
+    -e "s|@CSSMIN@|$CSSMIN|" \
+    -e "s|@GIT_VERSION@|$GIT_VERSION|" \
+    -e "s|@GIT_BINDIR@|$GIT_BINDIR|" \
+    -e "s|@GITWEB_CONFIG@|$GITWEB_CONFIG|" \
+    -e "s|@GITWEB_CONFIG_SYSTEM@|$GITWEB_CONFIG_SYSTEM|" \
+    -e "s|@GITWEB_CONFIG_COMMON@|$GITWEB_CONFIG_COMMON|" \
+    -e "s|@GITWEB_HOME_LINK_STR@|$GITWEB_HOME_LINK_STR|" \
+    -e "s|@GITWEB_SITENAME@|$GITWEB_SITENAME|" \
+    -e "s|@GITWEB_PROJECTROOT@|$GITWEB_PROJECTROOT|" \
+    -e "s|@GITWEB_PROJECT_MAXDEPTH@|$GITWEB_PROJECT_MAXDEPTH|" \
+    -e "s|@GITWEB_EXPORT_OK@|$GITWEB_EXPORT_OK|" \
+    -e "s|@GITWEB_STRICT_EXPORT@|$GITWEB_STRICT_EXPORT|" \
+    -e "s|@GITWEB_BASE_URL@|$GITWEB_BASE_URL|" \
+    -e "s|@GITWEB_LIST@|$GITWEB_LIST|" \
+    -e "s|@GITWEB_HOMETEXT@|$GITWEB_HOMETEXT|" \
+    -e "s|@GITWEB_CSS@|$GITWEB_CSS|" \
+    -e "s|@GITWEB_LOGO@|$GITWEB_LOGO|" \
+    -e "s|@GITWEB_FAVICON@|$GITWEB_FAVICON|" \
+    -e "s|@GITWEB_JS@|$GITWEB_JS|" \
+    -e "s|@GITWEB_SITE_HTML_HEAD_STRING@|$GITWEB_SITE_HTML_HEAD_STRING|" \
+    -e "s|@GITWEB_SITE_HEADER@|$GITWEB_SITE_HEADER|" \
+    -e "s|@GITWEB_SITE_FOOTER@|$GITWEB_SITE_FOOTER|" \
+    -e "s|@HIGHLIGHT_BIN@|$HIGHLIGHT_BIN|" \
+    "$INPUT" >"$OUTPUT"
+
+chmod a+x "$OUTPUT"
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 76e1f4e244fd23dd29b935e83753efa9c6d5bb5e..41bc64ec73f9f05666480025c3b70650607b4cd8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -88,7 +88,7 @@ sub evaluate_uri {
 
 # fs traversing limit for getting project list
 # the number is relative to the projectroot
-our $project_maxdepth = "@GITWEB_PROJECT_MAXDEPTH@";
+our $project_maxdepth = @GITWEB_PROJECT_MAXDEPTH@;
 
 # string of the home link on top of all pages
 our $home_link_str = "@GITWEB_HOME_LINK_STR@";
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 09/22] Makefile: extract script to massage Shell scripts

Same as in the preceding commits, extract a script that allows us to unify how we massage shell scripts.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-BUILD-OPTIONS.in                |  4 ++++
 Makefile                            | 34 ++++++++++------------------------
 contrib/buildsystems/CMakeLists.txt | 31 ++++++++++++++++++++-----------
 generate-script.sh                  | 34 ++++++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 35 deletions(-)
Show changes to 4 files +68 −35

GIT-BUILD-OPTIONS.in, Makefile, contrib/buildsystems/CMakeLists.txt, generate-script.sh

diff --git a/GIT-BUILD-OPTIONS.in b/GIT-BUILD-OPTIONS.in
index 050432f9fc49f93d0f6ed98c1307405c52761be0..9b95a6b3eee334b6f691702696ca9d92201b6bac 100644
--- a/GIT-BUILD-OPTIONS.in
+++ b/GIT-BUILD-OPTIONS.in
@@ -36,3 +36,7 @@ GIT_INTEROP_MAKE_OPTS=@GIT_INTEROP_MAKE_OPTS@
 GIT_TEST_INDEX_VERSION=@GIT_TEST_INDEX_VERSION@
 GIT_TEST_PERL_FATAL_WARNINGS=@GIT_TEST_PERL_FATAL_WARNINGS@
 RUNTIME_PREFIX=@RUNTIME_PREFIX@
+GITWEBDIR=@GITWEBDIR@
+USE_GETTEXT_SCHEME=@USE_GETTEXT_SCHEME@
+LOCALEDIR=@LOCALEDIR@
+BROKEN_PATH_FIX=@BROKEN_PATH_FIX@
diff --git a/Makefile b/Makefile
index 8aaa5bbfe2bd11aa88b7578e17b9839b1b9b0a0b..d921c4d8e2c362b2f6f95984fca4a11a02c9967a 100644
--- a/Makefile
+++ b/Makefile
@@ -1558,10 +1558,10 @@ endif
 
 ifdef SANE_TOOL_PATH
 SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
-BROKEN_PATH_FIX = 's|^\# @BROKEN_PATH_FIX@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
+BROKEN_PATH_FIX = s|^\# @BROKEN_PATH_FIX@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|
 PATH := $(SANE_TOOL_PATH):${PATH}
 else
-BROKEN_PATH_FIX = '/^\# @BROKEN_PATH_FIX@$$/d'
+BROKEN_PATH_FIX = /^\# @BROKEN_PATH_FIX@$$/d
 endif
 
 ifeq (,$(HOST_CPU))
@@ -2546,26 +2546,8 @@ GIT-SCRIPT-DEFINES: FORCE
 		echo "$$FLAGS" >$@; \
             fi
 
-define cmd_munge_script
-sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-    -e 's|@DIFF@|$(DIFF_SQ)|' \
-    -e 's|@LOCALEDIR@|$(localedir_SQ)|g' \
-    -e 's/@USE_GETTEXT_SCHEME@/$(USE_GETTEXT_SCHEME)/g' \
-    -e $(BROKEN_PATH_FIX) \
-    -e 's|@GITWEBDIR@|$(gitwebdir_SQ)|g' \
-    -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' \
-    -e 's|@PAGER_ENV@|$(PAGER_ENV_SQ)|g' \
-    $@.sh >$@+
-endef
-
-$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
-	$(QUIET_GEN)$(cmd_munge_script) && \
-	chmod +x $@+ && \
-	mv $@+ $@
-
-$(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
-	$(QUIET_GEN)$(cmd_munge_script) && \
+$(SCRIPT_SH_GEN) $(SCRIPT_LIB) : % : %.sh generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES
+	$(QUIET_GEN)./generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \
 	mv $@+ $@
 
 git.rc: git.rc.in GIT-VERSION-GEN GIT-VERSION-FILE
@@ -2635,8 +2617,8 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
 perllibdir:
 	@echo '$(perllibdir_SQ)'
 
-git-instaweb: git-instaweb.sh GIT-SCRIPT-DEFINES
-	$(QUIET_GEN)$(cmd_munge_script) && \
+git-instaweb: git-instaweb.sh generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES
+	$(QUIET_GEN)./generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \
 	chmod +x $@+ && \
 	mv $@+ $@
 else # NO_PERL
@@ -3193,6 +3175,10 @@ GIT-BUILD-OPTIONS: FORCE
 		-e "s|@GIT_TEST_INDEX_VERSION@|\'$(GIT_TEST_INDEX_VERSION)\'|" \
 		-e "s|@GIT_TEST_PERL_FATAL_WARNINGS@|\'$(GIT_TEST_PERL_FATAL_WARNINGS)\'|" \
 		-e "s|@RUNTIME_PREFIX@|\'$(RUNTIME_PREFIX)\'|" \
+		-e "s|@GITWEBDIR@|\'$(gitwebdir_SQ)\'|" \
+		-e "s|@USE_GETTEXT_SCHEME@|\'$(USE_GETTEXT_SCHEME)\'|" \
+		-e "s|@LOCALEDIR@|\'$(localedir_SQ)\'|" \
+		-e "s|@BROKEN_PATH_FIX@|\'$(BROKEN_PATH_FIX)\'|" \
 		GIT-BUILD-OPTIONS.in >$@+
 	@if grep -q '^[A-Z][A-Z_]*=@.*@$$' $@+; then echo "Unsubstituted build options in $@" >&2 && exit 1; fi
 	@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 0f9180937e76cad4ad831d34877ea276413e76c9..7121fdfbc2ba50b4c7afb3f2b45fdaa5b9909a60 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -838,18 +838,23 @@ set(git_shell_scripts
 	${git_sh_scripts} ${git_shlib_scripts} git-instaweb)
 
 foreach(script ${git_shell_scripts})
-	file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.sh content NEWLINE_CONSUME)
-	string(REPLACE "@SHELL_PATH@" "${SHELL_PATH}" content "${content}")
-	string(REPLACE "@DIFF@" "diff" content "${content}")
-	string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" content "${content}")
-	string(REPLACE "@GITWEBDIR@" "${GITWEBDIR}" content "${content}")
-	string(REPLACE "@NO_CURL@" "" content "${content}")
-	string(REPLACE "@USE_GETTEXT_SCHEME@" "" content "${content}")
-	string(REPLACE "# @BROKEN_PATH_FIX@" "" content "${content}")
-	string(REPLACE "@PERL_PATH@" "${PERL_PATH}" content "${content}")
-	string(REPLACE "@PAGER_ENV@" "LESS=FRX LV=-c" content "${content}")
-	file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
+	if ("${script}" IN_LIST git_sh_scripts)
+		string(REPLACE ".sh" "" shell_gen_path "${script}")
+	else()
+		set(shell_gen_path "${script}")
+	endif()
+
+	add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${shell_gen_path}"
+		COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-script.sh"
+			"${CMAKE_SOURCE_DIR}/${script}.sh"
+			"${CMAKE_BINARY_DIR}/${shell_gen_path}"
+			"${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS"
+		DEPENDS "${CMAKE_SOURCE_DIR}/generate-script.sh"
+			"${CMAKE_SOURCE_DIR}/${script}.sh"
+		VERBATIM)
+	list(APPEND shell_gen ${CMAKE_BINARY_DIR}/${shell_gen_path})
 endforeach()
+add_custom_target(shell-gen ALL DEPENDS ${shell_gen})
 
 #perl scripts
 parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" "")
@@ -1175,6 +1180,10 @@ string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_optio
 string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
 string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
 string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
+string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
+string(REPLACE "@USE_GETTEXT_SCHEME@" "" git_build_options "${git_build_options}")
+string(REPLACE "@LOCALEDIR@" "'${LOCALEDIR}'" git_build_options "${git_build_options}")
+string(REPLACE "@BROKEN_PATH_FIX@" "" git_build_options "${git_build_options}")
 if(USE_VCPKG)
 	string(APPEND git_build_options "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
 endif()
diff --git a/generate-script.sh b/generate-script.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d001e43d7bf1c4d72f41f805128ae42330a54558
--- /dev/null
+++ b/generate-script.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+set -e
+
+if test $# -ne 3
+then
+	echo "USAGE: $0 <INPUT> <OUTPUT> <GIT-BUILD-OPTIONS>" >&2
+	exit 1
+fi
+
+INPUT="$1"
+OUTPUT="$2"
+BUILD_OPTIONS="$3"
+
+. "$BUILD_OPTIONS"
+
+sed -e "1s|#!.*/sh|#!$SHELL_PATH|" \
+    -e "s|@SHELL_PATH@|$SHELL_PATH|" \
+    -e "s|@DIFF@|$DIFF|" \
+    -e "s|@LOCALEDIR@|$LOCALEDIR|g" \
+    -e "s/@USE_GETTEXT_SCHEME@/$USE_GETTEXT_SCHEME/g" \
+    -e "$BROKEN_PATH_FIX" \
+    -e "s|@GITWEBDIR@|$GITWEBDIR|g" \
+    -e "s|@PERL_PATH@|$PERL_PATH|g" \
+    -e "s|@PAGER_ENV@|$PAGER_ENV|g" \
+    "$INPUT" >"$OUTPUT"
+
+case "$(basename "$INPUT")" in
+git-mergetool--lib.sh|git-sh-i18n.sh|git-sh-setup.sh)
+	;;
+*)
+	chmod a+x "$OUTPUT"
+	;;
+esac
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 11/22] Makefile: extract script to generate gitweb.js

Similar to the preceding commit, also extract the script to generate the "gitweb.js" file. While the logic itself is trivial, it helps us avoid duplication of logic across build systems and ensures that the build systems will remain in sync with each other in case the logic ever needs to change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 gitweb/Makefile              |  3 ++-
 gitweb/generate-gitweb-js.sh | 12 ++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)
Show changes to 2 files +14 −1

gitweb/Makefile, gitweb/generate-gitweb-js.sh

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 16a2ef2d1e5664d99f6f1d8ff4224c36769c55fb..d5748e93594eb6181269c2fe272bf7ef980cbe68 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -115,9 +115,10 @@ $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)gitweb.perl
 	$(MAK_DIR_GITWEB)generate-gitweb-cgi.sh $(MAK_DIR_GITWEB)/GITWEB-BUILD-OPTIONS ./GIT-VERSION-FILE $< $@+ && \
 	mv $@+ $@
 
+$(MAK_DIR_GITWEB)static/gitweb.js: $(MAK_DIR_GITWEB)generate-gitweb-js.sh
 $(MAK_DIR_GITWEB)static/gitweb.js: $(addprefix $(MAK_DIR_GITWEB),$(GITWEB_JSLIB_FILES))
 	$(QUIET_GEN)$(RM) $@ $@+ && \
-	cat $^ >$@+ && \
+	$(MAK_DIR_GITWEB)generate-gitweb-js.sh $@+ $^ && \
 	mv $@+ $@
 
 ### Installation rules
diff --git a/gitweb/generate-gitweb-js.sh b/gitweb/generate-gitweb-js.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bb81220da393ebecae28b675c299b626f0e698fb
--- /dev/null
+++ b/gitweb/generate-gitweb-js.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if test "$#" -lt 2
+then
+	echo "USAGE: $0 <OUTPUT> <INPUT>..." >&2
+	exit 1
+fi
+
+OUTPUT="$1"
+shift
+
+cat "$@" >"$OUTPUT"
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 12/22] Makefile: refactor generators to be PWD-independent

We have multiple scripts that generate headers from other data. All of these scripts have the assumption built-in that they are executed in the current source directory, which makes them a bit unwieldy to use during out-of-tree builds.

Refactor them to instead take the source directory as well as the output file as arguments.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Makefile                            |  6 +++---
 contrib/buildsystems/CMakeLists.txt | 19 +++++++++--------
 generate-cmdlist.sh                 | 42 +++++++++++++++++++++++--------------
 generate-configlist.sh              | 20 +++++++++++++-----
 generate-hooklist.sh                | 15 ++++++++++++-
 5 files changed, 68 insertions(+), 34 deletions(-)
Show changes to 5 files +68 −34

Makefile, contrib/buildsystems/CMakeLists.txt, generate-cmdlist.sh, generate-configlist.sh, generate-hooklist.sh

diff --git a/Makefile b/Makefile
index d921c4d8e2c362b2f6f95984fca4a11a02c9967a..d9a0025b30db24cb035f19812f0071fe0642386b 100644
--- a/Makefile
+++ b/Makefile
@@ -2523,17 +2523,17 @@ $(BUILT_INS): git$X
 config-list.h: generate-configlist.sh
 
 config-list.h: Documentation/*config.txt Documentation/config/*.txt
-	$(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh >$@
+	$(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh . $@
 
 command-list.h: generate-cmdlist.sh command-list.txt
 
 command-list.h: $(wildcard Documentation/git*.txt)
 	$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
 		$(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
-		command-list.txt >$@
+		. $@
 
 hook-list.h: generate-hooklist.sh Documentation/githooks.txt
-	$(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh >$@
+	$(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh . $@
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):\
 	$(localedir_SQ):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 7121fdfbc2ba50b4c7afb3f2b45fdaa5b9909a60..be1ce1f37a775c366098717047cd7579cd81015d 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -638,23 +638,24 @@ set(EXCLUSION_PROGS_CACHE ${EXCLUSION_PROGS} CACHE STRING "Programs not built" F
 if(NOT EXISTS ${CMAKE_BINARY_DIR}/command-list.h OR NOT EXCLUSION_PROGS_CACHE STREQUAL EXCLUSION_PROGS)
 	list(REMOVE_ITEM EXCLUSION_PROGS empty)
 	message("Generating command-list.h")
-	execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-cmdlist.sh ${EXCLUSION_PROGS} command-list.txt
-			WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-			OUTPUT_FILE ${CMAKE_BINARY_DIR}/command-list.h)
+	execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-cmdlist.sh"
+				${EXCLUSION_PROGS}
+				"${CMAKE_SOURCE_DIR}"
+				"${CMAKE_BINARY_DIR}/command-list.h")
 endif()
 
 if(NOT EXISTS ${CMAKE_BINARY_DIR}/config-list.h)
 	message("Generating config-list.h")
-	execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-configlist.sh
-			WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-			OUTPUT_FILE ${CMAKE_BINARY_DIR}/config-list.h)
+	execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-configlist.sh"
+				"${CMAKE_SOURCE_DIR}"
+				"${CMAKE_BINARY_DIR}/config-list.h")
 endif()
 
 if(NOT EXISTS ${CMAKE_BINARY_DIR}/hook-list.h)
 	message("Generating hook-list.h")
-	execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-hooklist.sh
-			WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-			OUTPUT_FILE ${CMAKE_BINARY_DIR}/hook-list.h)
+	execute_process(COMMAND "${SH_EXE}" ${CMAKE_SOURCE_DIR}/generate-hooklist.sh
+				"${CMAKE_SOURCE_DIR}"
+				"${CMAKE_BINARY_DIR}/hook-list.h")
 endif()
 
 include_directories(${CMAKE_BINARY_DIR})
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 205541e0f7f81b1df4061215ae34a2742a45475d..b923a5aab80dfa571a40a4f6fa3d860fcf8f8dd1 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -64,7 +64,7 @@ define_category_names () {
 print_command_list () {
 	echo "static struct cmdname_help command_list[] = {"
 
-	echo "$1" |
+	echo "$2" |
 	while read cmd rest
 	do
 		synopsis=
@@ -76,7 +76,7 @@ print_command_list () {
 				break
 				;;
 			esac
-		done <"Documentation/$cmd.txt"
+		done <"$1/Documentation/$cmd.txt"
 
 		printf '\t{ "%s", N_("%s"), 0' "$cmd" "$synopsis"
 		printf " | CAT_%s" $rest
@@ -93,18 +93,28 @@ do
 	shift
 done
 
-commands="$(command_list "$1")"
-categories="$(category_list "$commands")"
+if test "$#" -ne 2
+then
+	die "USAGE: $0 <SOURCE_DIR> <OUTPUT>"
+fi
+
+SOURCE_DIR="$1"
+OUTPUT="$2"
+
+{
+	commands="$(command_list "$SOURCE_DIR"/command-list.txt)"
+	categories="$(category_list "$commands")"
 
-echo "/* Automatically generated by generate-cmdlist.sh */
-struct cmdname_help {
-	const char *name;
-	const char *help;
-	uint32_t category;
-};
-"
-define_categories "$categories"
-echo
-define_category_names "$categories"
-echo
-print_command_list "$commands"
+	echo "/* Automatically generated by generate-cmdlist.sh */
+	struct cmdname_help {
+		const char *name;
+		const char *help;
+		uint32_t category;
+	};
+	"
+	define_categories "$categories"
+	echo
+	define_category_names "$categories"
+	echo
+	print_command_list "$SOURCE_DIR" "$commands"
+} >"$OUTPUT"
diff --git a/generate-configlist.sh b/generate-configlist.sh
index 8692fe5cf4d5e4025265e6110254b05640615fea..512804a1ca1b63a0630ae152569b31486273eae0 100755
--- a/generate-configlist.sh
+++ b/generate-configlist.sh
@@ -1,13 +1,19 @@
 #!/bin/sh
 
-echo "/* Automatically generated by generate-configlist.sh */"
-echo
+SOURCE_DIR="$1"
+OUTPUT="$2"
+
+if test -z "$SOURCE_DIR" || ! test -d "$SOURCE_DIR" || test -z "$OUTPUT"
+then
+	echo "USAGE: $0 <SOURCE_DIR> <OUTPUT>" >&2
+	exit 1
+fi
 
 print_config_list () {
 	cat <<EOF
 static const char *config_name_list[] = {
 EOF
-	grep -h '^[a-zA-Z].*\..*::$' Documentation/*config.txt Documentation/config/*.txt |
+	grep -h '^[a-zA-Z].*\..*::$' "$SOURCE_DIR"/Documentation/*config.txt "$SOURCE_DIR"/Documentation/config/*.txt |
 	sed '/deprecated/d; s/::$//; s/,  */\n/g' |
 	sort |
 	sed 's/^.*$/	"&",/'
@@ -17,5 +23,9 @@ EOF
 EOF
 }
 
-echo
-print_config_list
+{
+	echo "/* Automatically generated by generate-configlist.sh */"
+	echo
+	echo
+	print_config_list
+} >"$OUTPUT"
diff --git a/generate-hooklist.sh b/generate-hooklist.sh
index 2f9f54eb545be2bd79c37a868dbeef96d2ddfb9f..0ff2a0b6fbd16772de1f88cad36af8185892bb47 100755
--- a/generate-hooklist.sh
+++ b/generate-hooklist.sh
@@ -2,6 +2,17 @@
 #
 # Usage: ./generate-hooklist.sh >hook-list.h
 
+SOURCE_DIR="$1"
+OUTPUT="$2"
+
+if test -z "$SOURCE_DIR" || ! test -d "$SOURCE_DIR" || test -z "$OUTPUT"
+then
+	echo "USAGE: $0 <SOURCE_DIR> <OUTPUT>" >&2
+	exit 1
+fi
+
+{
+
 cat <<EOF
 /* Automatically generated by generate-hooklist.sh */
 
@@ -11,10 +22,12 @@ EOF
 sed -n \
 	-e '/^~~~~*$/ {x; s/^.*$/	"&",/; p;}' \
 	-e 'x' \
-	<Documentation/githooks.txt |
+	<"$SOURCE_DIR"/Documentation/githooks.txt |
 	LC_ALL=C sort
 
 cat <<EOF
 	NULL,
 };
 EOF
+
+} >"$OUTPUT"
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 13/22] Makefile: allow "bin-wrappers/" directory to exist

The "bin-wrappers/" directory gets created by our build system and is populated with one script for each of our binaries. There isn't anything inherently wrong with the current layout, but it is somewhat hard to adapt for out-of-tree build systems.

Adapt the layout such that our "bin-wrappers/" directory always exists and contains our "wrap-for-bin.sh" script to make things a little bit easier for subsequent steps.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 .gitignore                                      | 1 -
 Documentation/CodingGuidelines                  | 2 +-
 Makefile                                        | 6 +++---
 bin-wrappers/.gitignore                         | 9 +++++++++
 wrap-for-bin.sh => bin-wrappers/wrap-for-bin.sh | 0
 contrib/buildsystems/CMakeLists.txt             | 6 +++---
 6 files changed, 16 insertions(+), 8 deletions(-)
Show changes to 6 files +16 −8

.gitignore, Documentation/CodingGuidelines, Makefile, bin-wrappers/.gitignore, wrap-for-bin.sh, contrib/buildsystems/CMakeLists.txt

diff --git a/.gitignore b/.gitignore
index d3be460040c6120d862a25dff1abd325b93b37fd..e82aa19df03fc865eaa0468f12b734cec0c48a2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,6 @@
 /GIT-TEST-SUITES
 /GIT-USER-AGENT
 /GIT-VERSION-FILE
-/bin-wrappers/
 /git
 /git-add
 /git-am
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 87904791cbcea6e57d0fb3c7d4e27f2b0d80b95f..1df9d0c42f1ee7b16ecd7abc22d2653dbf9bae2e 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -583,7 +583,7 @@ For C programs:
    Run `GIT_DEBUGGER=1 ./bin-wrappers/git foo` to simply use gdb as is, or
    run `GIT_DEBUGGER="<debugger> <debugger-args>" ./bin-wrappers/git foo` to
    use your own debugger and arguments. Example: `GIT_DEBUGGER="ddd --gdb"
-   ./bin-wrappers/git log` (See `wrap-for-bin.sh`.)
+   ./bin-wrappers/git log` (See `bin-wrappers/wrap-for-bin.sh`.)
 
  - The primary data structure that a subsystem 'S' deals with is called
    `struct S`. Functions that operate on `struct S` are named
diff --git a/Makefile b/Makefile
index d9a0025b30db24cb035f19812f0071fe0642386b..056234e0bc0722ebc9f5d6ccae97f98fc9a64a37 100644
--- a/Makefile
+++ b/Makefile
@@ -3200,8 +3200,7 @@ test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
 
-bin-wrappers/%: wrap-for-bin.sh
-	$(call mkdir_p_parent_template)
+$(test_bindir_programs): bin-wrappers/%: bin-wrappers/wrap-for-bin.sh
 	$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 	     -e 's|@BUILD_DIR@|$(shell pwd)|' \
 	     -e 's|@PROG@|$(patsubst test-%,t/helper/test-%,$(@F))$(if $(filter-out $(BINDIR_PROGRAMS_NO_X),$(@F)),$(X),)|' < $< > $@ && \
@@ -3697,7 +3696,8 @@ clean: profile-clean coverage-clean cocciclean
 	$(RM) $(FUZZ_PROGRAMS)
 	$(RM) $(SP_OBJ)
 	$(RM) $(HCC)
-	$(RM) -r bin-wrappers $(dep_dirs) $(compdb_dir) compile_commands.json
+	$(RM) -r $(dep_dirs) $(compdb_dir) compile_commands.json
+	$(RM) $(test_bindir_programs)
 	$(RM) -r po/build/
 	$(RM) *.pyc *.pyo */*.pyc */*.pyo $(GENERATED_H) $(ETAGS_TARGET) tags cscope*
 	$(RM) -r .dist-tmp-dir .doc-tmp-dir
diff --git a/bin-wrappers/.gitignore b/bin-wrappers/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1c6c90458b758641d0311e812563b32dd4656b48
--- /dev/null
+++ b/bin-wrappers/.gitignore
@@ -0,0 +1,9 @@
+/git
+/git-cvsserver
+/git-receive-pack
+/git-shell
+/git-upload-archive
+/git-upload-pack
+/scalar
+/test-fake-ssh
+/test-tool
diff --git a/wrap-for-bin.sh b/bin-wrappers/wrap-for-bin.sh
old mode 100644
new mode 100755
similarity index 100%
rename from wrap-for-bin.sh
rename to bin-wrappers/wrap-for-bin.sh
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index be1ce1f37a775c366098717047cd7579cd81015d..6fc4f051400db00761232d0aaf1bc88634022b5b 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1080,20 +1080,20 @@ set(wrapper_test_scripts
 
 
 foreach(script ${wrapper_scripts})
-	file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+	file(STRINGS ${CMAKE_SOURCE_DIR}/bin-wrappers/wrap-for-bin.sh content NEWLINE_CONSUME)
 	string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
 	string(REPLACE "@PROG@" "${script}${EXE_EXTENSION}" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
 endforeach()
 
 foreach(script ${wrapper_test_scripts})
-	file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+	file(STRINGS ${CMAKE_SOURCE_DIR}/bin-wrappers/wrap-for-bin.sh content NEWLINE_CONSUME)
 	string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
 	string(REPLACE "@PROG@" "t/helper/${script}${EXE_EXTENSION}" content "${content}")
 	file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
 endforeach()
 
-file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+file(STRINGS ${CMAKE_SOURCE_DIR}/bin-wrappers/wrap-for-bin.sh content NEWLINE_CONSUME)
 string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
 string(REPLACE "@PROG@" "git-cvsserver" content "${content}")
 file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 14/22] Makefile: simplify building of templates

When we install Git we also install a set of default templates that both git-init(1) and git-clone(1) populate into our build directories. The way the pristine templates are laid out in our source directory is somewhat weird though: instead of reconstructing the actual directory hierarchy in "templates/", we represent directory separators with "--".

The only reason I could come up with for why we have this is the "branches/" directory, which is supposed to be empty when installing it. And as Git famously doesn't store empty directories at all we have to work around this limitation.

Now the thing is that the "branches/" directory is a leftover to how branches used to be stored in the dark ages. gitrepository-layout(5) lists this directory as "slightly deprecated", which I would claim is a strong understatement. I have never encountered anybody using it today and would be surprised if it even works as expected. So having the "--" hack in place for an item that is basically unused, unmaintained and deprecated doesn't only feel unreasonable, but installing that entry by default may also cause confusion for users that do not know what this is supposed to be in the first place.

Remove this directory from our templates and, now that we do not require the workaround anymore, restructure the templates to form a proper hierarchy. This makes it way easier for build systems to install these templates into place.

We should likely think about removing support for "branch/" altogether, but that is outside of the scope of this patch series.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 contrib/buildsystems/CMakeLists.txt                | 34 +++++++------------
 templates/Makefile                                 | 39 ++++++++++++++--------
 templates/branches--                               |  1 -
 templates/{this--description => description}       |  0
 .../applypatch-msg.sample}                         |  0
 .../commit-msg.sample}                             |  0
 .../fsmonitor-watchman.sample}                     |  0
 .../post-update.sample}                            |  0
 .../pre-applypatch.sample}                         |  0
 .../pre-commit.sample}                             |  0
 .../pre-merge-commit.sample}                       |  0
 .../pre-push.sample}                               |  0
 .../pre-rebase.sample}                             |  0
 .../pre-receive.sample}                            |  0
 .../prepare-commit-msg.sample}                     |  0
 .../push-to-checkout.sample}                       |  0
 .../sendemail-validate.sample}                     |  0
 .../{hooks--update.sample => hooks/update.sample}  |  0
 templates/{info--exclude => info/exclude}          |  0
 19 files changed, 37 insertions(+), 37 deletions(-)
Show changes to 19 files +37 −37

contrib/buildsystems/CMakeLists.txt, templates/Makefile, templates/branches--, templates/this--description, templates/hooks--applypatch-msg.sample, templates/hooks--commit-msg.sample, templates/hooks--fsmonitor-watchman.sample, templates/hooks--post-update.sample, templates/hooks--pre-applypatch.sample, templates/hooks--pre-commit.sample, templates/hooks--pre-merge-commit.sample, templates/hooks--pre-push.sample, templates/hooks--pre-rebase.sample, templates/hooks--pre-receive.sample, templates/hooks--prepare-commit-msg.sample, templates/hooks--push-to-checkout.sample, templates/hooks--sendemail-validate.sample, templates/hooks--update.sample, templates/info--exclude

diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 6fc4f051400db00761232d0aaf1bc88634022b5b..852b841601642402fb47489fba07445435e27930 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -99,8 +99,8 @@ project(git
 #TODO Enable NLS on windows natively
 
 #macros for parsing the Makefile for sources and scripts
-macro(parse_makefile_for_sources list_var regex)
-	file(STRINGS ${CMAKE_SOURCE_DIR}/Makefile ${list_var} REGEX "^${regex} \\+=(.*)")
+macro(parse_makefile_for_sources list_var makefile regex)
+	file(STRINGS ${makefile} ${list_var} REGEX "^${regex} \\+=(.*)")
 	string(REPLACE "${regex} +=" "" ${list_var} ${${list_var}})
 	string(REPLACE "$(COMPAT_OBJS)" "" ${list_var} ${${list_var}}) #remove "$(COMPAT_OBJS)" This is only for libgit.
 	string(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces
@@ -662,7 +662,7 @@ include_directories(${CMAKE_BINARY_DIR})
 
 #build
 #libgit
-parse_makefile_for_sources(libgit_SOURCES "LIB_OBJS")
+parse_makefile_for_sources(libgit_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "LIB_OBJS")
 
 list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
@@ -680,13 +680,13 @@ list(APPEND libgit_SOURCES "${CMAKE_BINARY_DIR}/version-def.h")
 add_library(libgit ${libgit_SOURCES} ${compat_SOURCES})
 
 #libxdiff
-parse_makefile_for_sources(libxdiff_SOURCES "XDIFF_OBJS")
+parse_makefile_for_sources(libxdiff_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "XDIFF_OBJS")
 
 list(TRANSFORM libxdiff_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 add_library(xdiff STATIC ${libxdiff_SOURCES})
 
 #reftable
-parse_makefile_for_sources(reftable_SOURCES "REFTABLE_OBJS")
+parse_makefile_for_sources(reftable_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "REFTABLE_OBJS")
 
 list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 add_library(reftable STATIC ${reftable_SOURCES})
@@ -757,7 +757,7 @@ elseif(UNIX)
 endif()
 
 #git
-parse_makefile_for_sources(git_SOURCES "BUILTIN_OBJS")
+parse_makefile_for_sources(git_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "BUILTIN_OBJS")
 
 list(TRANSFORM git_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 add_executable(git ${CMAKE_SOURCE_DIR}/git.c ${git_SOURCES})
@@ -905,24 +905,14 @@ file(STRINGS ${CMAKE_SOURCE_DIR}/git-p4.py content NEWLINE_CONSUME)
 string(REPLACE "#!/usr/bin/env python" "#!/usr/bin/python" content "${content}")
 file(WRITE ${CMAKE_BINARY_DIR}/git-p4 ${content})
 
-#templates
-file(GLOB templates "${CMAKE_SOURCE_DIR}/templates/*")
-list(TRANSFORM templates REPLACE "${CMAKE_SOURCE_DIR}/templates/" "")
-list(REMOVE_ITEM templates ".gitignore")
-list(REMOVE_ITEM templates "Makefile")
-list(REMOVE_ITEM templates "blt")# Prevents an error when reconfiguring for in source builds
-
-list(REMOVE_ITEM templates "branches--")
-file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/templates/blt/branches) #create branches
-
+#${CMAKE_SOURCE_DIR}/Makefile templates
+parse_makefile_for_sources(templates ${CMAKE_SOURCE_DIR}/templates/Makefile "TEMPLATES")
+string(REPLACE " " ";" templates ${templates})
 #templates have @.*@ replacement so use configure_file instead
 foreach(tm ${templates})
-	string(REPLACE "--" "/" blt_tm ${tm})
-	string(REPLACE "this" "" blt_tm ${blt_tm})# for this--
-	configure_file(${CMAKE_SOURCE_DIR}/templates/${tm} ${CMAKE_BINARY_DIR}/templates/blt/${blt_tm} @ONLY)
+	configure_file(${CMAKE_SOURCE_DIR}/templates/${tm} ${CMAKE_BINARY_DIR}/templates/blt/${tm} @ONLY)
 endforeach()
 
-
 #translations
 if(MSGFMT_EXE)
 	file(GLOB po_files "${CMAKE_SOURCE_DIR}/po/*.po")
@@ -996,7 +986,7 @@ add_executable(test-fake-ssh ${CMAKE_SOURCE_DIR}/t/helper/test-fake-ssh.c)
 target_link_libraries(test-fake-ssh common-main)
 
 #unit-tests
-parse_makefile_for_sources(unit-test_SOURCES "UNIT_TEST_OBJS")
+parse_makefile_for_sources(unit-test_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "UNIT_TEST_OBJS")
 list(TRANSFORM unit-test_SOURCES REPLACE "\\$\\(UNIT_TEST_DIR\\)/" "${CMAKE_SOURCE_DIR}/t/unit-tests/")
 add_library(unit-test-lib STATIC ${unit-test_SOURCES})
 
@@ -1054,7 +1044,7 @@ if(MSVC)
 endif()
 
 #test-tool
-parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+parse_makefile_for_sources(test-tool_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "TEST_BUILTINS_OBJS")
 add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
 
 list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
diff --git a/templates/Makefile b/templates/Makefile
index 367ad00c24cb34c0668eb5236dc3b08a477a65b7..bd1e9e30c120032b209fb1d221b9d93eea31c649 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -29,24 +29,35 @@ all: boilerplates.made custom
 # in a file direc--tory--file in the source.  They will be
 # just copied to the destination.
 
-bpsrc = $(filter-out %~,$(wildcard *--*))
-boilerplates.made : $(bpsrc)
-	$(QUIET)umask 022 && ls *--* 2>/dev/null | \
-	while read boilerplate; \
+TEMPLATES  =
+TEMPLATES += description
+TEMPLATES += hooks/applypatch-msg.sample
+TEMPLATES += hooks/commit-msg.sample
+TEMPLATES += hooks/fsmonitor-watchman.sample
+TEMPLATES += hooks/post-update.sample
+TEMPLATES += hooks/pre-applypatch.sample
+TEMPLATES += hooks/pre-commit.sample
+TEMPLATES += hooks/pre-merge-commit.sample
+TEMPLATES += hooks/prepare-commit-msg.sample
+TEMPLATES += hooks/pre-push.sample
+TEMPLATES += hooks/pre-rebase.sample
+TEMPLATES += hooks/pre-receive.sample
+TEMPLATES += hooks/push-to-checkout.sample
+TEMPLATES += hooks/sendemail-validate.sample
+TEMPLATES += hooks/update.sample
+TEMPLATES += info/exclude
+
+boilerplates.made: $(TEMPLATES)
+	$(QUIET)umask 022 && for template in $(TEMPLATES); \
 	do \
-		case "$$boilerplate" in *~) continue ;; esac && \
-		dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \
-		dir=`expr "$$dst" : '\(.*\)/'` && \
+		dir=$$(dirname "$$template") && \
 		mkdir -p blt/$$dir && \
-		case "$$boilerplate" in \
-		*--) continue;; \
-		esac && \
 		sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 		    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-		    -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' $$boilerplate > \
-			blt/$$dst && \
-		if test -x "$$boilerplate"; then rx=rx; else rx=r; fi && \
-		chmod a+$$rx "blt/$$dst" || exit; \
+		    -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' $$template > \
+			blt/$$template && \
+		if test -x "$$template"; then rx=rx; else rx=r; fi && \
+		chmod a+$$rx "blt/$$template" || exit; \
 	done && \
 	date >$@
 
diff --git a/templates/branches-- b/templates/branches--
deleted file mode 100644
index fae88709a636f3a06cc813dd64b28bfee7fa2073..0000000000000000000000000000000000000000
--- a/templates/branches--
+++ /dev/null
@@ -1 +0,0 @@
-: this is just to ensure the directory exists.
diff --git a/templates/this--description b/templates/description
similarity index 100%
rename from templates/this--description
rename to templates/description
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks/applypatch-msg.sample
similarity index 100%
rename from templates/hooks--applypatch-msg.sample
rename to templates/hooks/applypatch-msg.sample
diff --git a/templates/hooks--commit-msg.sample b/templates/hooks/commit-msg.sample
similarity index 100%
rename from templates/hooks--commit-msg.sample
rename to templates/hooks/commit-msg.sample
diff --git a/templates/hooks--fsmonitor-watchman.sample b/templates/hooks/fsmonitor-watchman.sample
similarity index 100%
rename from templates/hooks--fsmonitor-watchman.sample
rename to templates/hooks/fsmonitor-watchman.sample
diff --git a/templates/hooks--post-update.sample b/templates/hooks/post-update.sample
similarity index 100%
rename from templates/hooks--post-update.sample
rename to templates/hooks/post-update.sample
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks/pre-applypatch.sample
similarity index 100%
rename from templates/hooks--pre-applypatch.sample
rename to templates/hooks/pre-applypatch.sample
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks/pre-commit.sample
similarity index 100%
rename from templates/hooks--pre-commit.sample
rename to templates/hooks/pre-commit.sample
diff --git a/templates/hooks--pre-merge-commit.sample b/templates/hooks/pre-merge-commit.sample
similarity index 100%
rename from templates/hooks--pre-merge-commit.sample
rename to templates/hooks/pre-merge-commit.sample
diff --git a/templates/hooks--pre-push.sample b/templates/hooks/pre-push.sample
similarity index 100%
rename from templates/hooks--pre-push.sample
rename to templates/hooks/pre-push.sample
diff --git a/templates/hooks--pre-rebase.sample b/templates/hooks/pre-rebase.sample
similarity index 100%
rename from templates/hooks--pre-rebase.sample
rename to templates/hooks/pre-rebase.sample
diff --git a/templates/hooks--pre-receive.sample b/templates/hooks/pre-receive.sample
similarity index 100%
rename from templates/hooks--pre-receive.sample
rename to templates/hooks/pre-receive.sample
diff --git a/templates/hooks--prepare-commit-msg.sample b/templates/hooks/prepare-commit-msg.sample
similarity index 100%
rename from templates/hooks--prepare-commit-msg.sample
rename to templates/hooks/prepare-commit-msg.sample
diff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks/push-to-checkout.sample
similarity index 100%
rename from templates/hooks--push-to-checkout.sample
rename to templates/hooks/push-to-checkout.sample
diff --git a/templates/hooks--sendemail-validate.sample b/templates/hooks/sendemail-validate.sample
similarity index 100%
rename from templates/hooks--sendemail-validate.sample
rename to templates/hooks/sendemail-validate.sample
diff --git a/templates/hooks--update.sample b/templates/hooks/update.sample
similarity index 100%
rename from templates/hooks--update.sample
rename to templates/hooks/update.sample
diff --git a/templates/info--exclude b/templates/info/exclude
similarity index 100%
rename from templates/info--exclude
rename to templates/info/exclude
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 16/22] Documentation: teach "cmd-list.perl" about out-of-tree builds

The "cmd-list.perl" script generates a list of commands that can be included into our manpages. The script doesn't know about out-of-tree builds and instead writes resulting files into the source directory.

Adapt it such that we can read data from the source directory and write data into the build directory.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/Makefile      |  2 +-
 Documentation/cmd-list.perl | 23 ++++++++++++-----------
 2 files changed, 13 insertions(+), 12 deletions(-)
Show changes to 2 files +13 −12

Documentation/Makefile, Documentation/cmd-list.perl

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 75755ceec189836813e01fa43bae69bdd2083f3e..2b9fd37ff7021f3d44eaa282b942c623f403510a 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -306,7 +306,7 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
 $(cmds_txt): cmd-list.made
 
 cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
-	$(QUIET_GEN)$(PERL_PATH) ./cmd-list.perl ../command-list.txt $(cmds_txt) $(QUIET_STDERR) && \
+	$(QUIET_GEN)$(PERL_PATH) ./cmd-list.perl .. . $(cmds_txt) && \
 	date >$@
 
 mergetools_txt = mergetools-diff.txt mergetools-merge.txt
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 755a110bc48d7e2b596651ca13664c01192d966c..e260a989774071b66d2b56c56c5045b84a508c5c 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -3,12 +3,13 @@
 use File::Compare qw(compare);
 
 sub format_one {
-	my ($out, $nameattr) = @_;
+	my ($source_dir, $out, $nameattr) = @_;
 	my ($name, $attr) = @$nameattr;
+	my ($path) = "$source_dir/Documentation/$name.txt";
 	my ($state, $description);
 	my $mansection;
 	$state = 0;
-	open I, '<', "$name.txt" or die "No such file $name.txt";
+	open I, '<', "$path" or die "No such file $path.txt";
 	while (<I>) {
 		if (/^(?:git|scalar)[a-z0-9-]*\(([0-9])\)$/) {
 			$mansection = $1;
@@ -29,7 +30,7 @@ sub format_one {
 	}
 	close I;
 	if (!defined $description) {
-		die "No description found in $name.txt";
+		die "No description found in $path.txt";
 	}
 	if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) {
 		print $out "linkgit:$name\[$mansection\]::\n\t";
@@ -43,9 +44,9 @@ sub format_one {
 	}
 }
 
-my ($input, @categories) = @ARGV;
+my ($source_dir, $build_dir, @categories) = @ARGV;
 
-open IN, "<$input";
+open IN, "<$source_dir/command-list.txt";
 while (<IN>) {
 	last if /^### command list/;
 }
@@ -63,17 +64,17 @@ sub format_one {
 
 for my $out (@categories) {
 	my ($cat) = $out =~ /^cmds-(.*)\.txt$/;
-	open O, '>', "$out+" or die "Cannot open output file $out+";
+	my ($path) = "$build_dir/$out";
+	open O, '>', "$path+" or die "Cannot open output file $out+";
 	for (@{$cmds{$cat}}) {
-		format_one(\*O, $_);
+		format_one($source_dir, \*O, $_);
 	}
 	close O;
 
-	if (-f "$out" && compare("$out", "$out+") == 0) {
-		unlink "$out+";
+	if (-f "$path" && compare("$path", "$path+") == 0) {
+		unlink "$path+";
 	}
 	else {
-		print STDERR "$out\n";
-		rename "$out+", "$out";
+		rename "$path+", "$path";
 	}
 }
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 15/22] Documentation: allow sourcing generated includes from separate dir

Our documentation uses "include::" directives to include parts that are either reused across multiple documents or parts that we generate at build time. Unfortunately, top-level includes are only ever resolved relative to the base directory, which is typically the directory of the including document. Most importantly, it is not possible to have either asciidoc or asciidoctor search multiple directories.

It follows that both kinds of includes must live in the same directory. This is of course a bummer for out-of-tree builds, because here the dynamically-built includes live in the build directory whereas the static includes live in the source directory.

Introduce a `build_dir` attribute and prepend it to all of our includes for dynamically-built files. This attribute gets set to the build directory and thus converts the include path to an absolute path, which asciidoc and asciidoctor know how to resolve.

Note that this change also requires us to update "build-docdep.perl", which tries to figure out included files such our Makefile can set up proper build-time dependencies. This script simply scans through the source files for any lines that match "^include::" and treats the remainder of the line as included file path. But given that those may now contain the "{build_dir}" variable we have to teach the script to replace that attribute with the actual build directory.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/Makefile          |  3 ++-
 Documentation/build-docdep.perl |  2 ++
 Documentation/config/diff.txt   |  2 +-
 Documentation/config/merge.txt  |  2 +-
 Documentation/git.txt           | 24 ++++++++++++------------
 5 files changed, 18 insertions(+), 15 deletions(-)
Show changes to 5 files +18 −15

Documentation/Makefile, Documentation/build-docdep.perl, Documentation/config/diff.txt, Documentation/config/merge.txt, Documentation/git.txt

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 0f55baa252fffbcd506f0c6f410b26b37f2e9b11..75755ceec189836813e01fa43bae69bdd2083f3e 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -218,6 +218,7 @@ SHELL_PATH ?= $(SHELL)
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
+ASCIIDOC_EXTRA += -abuild_dir='$(shell pwd)'
 ifdef DEFAULT_PAGER
 DEFAULT_PAGER_SQ = $(subst ','\'',$(DEFAULT_PAGER))
 ASCIIDOC_EXTRA += -a 'git-default-pager=$(DEFAULT_PAGER_SQ)'
@@ -283,7 +284,7 @@ docdep_prereqs = \
 	cmd-list.made $(cmds_txt)
 
 doc.dep : $(docdep_prereqs) $(DOC_DEP_TXT) build-docdep.perl
-	$(QUIET_GEN)$(PERL_PATH) ./build-docdep.perl >$@ $(QUIET_STDERR)
+	$(QUIET_GEN)$(PERL_PATH) ./build-docdep.perl "$(shell pwd)" >$@ $(QUIET_STDERR)
 
 ifneq ($(MAKECMDGOALS),clean)
 -include doc.dep
diff --git a/Documentation/build-docdep.perl b/Documentation/build-docdep.perl
index 1b3ac8fdd95fa1d57afbdffb1a9743f36ed22fdd..315efaa2fa2f2efc89b2c8073cc97cd2cc83611c 100755
--- a/Documentation/build-docdep.perl
+++ b/Documentation/build-docdep.perl
@@ -1,5 +1,6 @@
 #!/usr/bin/perl
 
+my ($build_dir) = @ARGV;
 my %include = ();
 my %included = ();
 
@@ -10,6 +11,7 @@
 	    chomp;
 	    s/^include::\s*//;
 	    s/\[\]//;
+	    s/{build_dir}/${build_dir}/;
 	    $include{$text}{$_} = 1;
 	    $included{$_} = 1;
 	}
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 190bda17e51c57a00b25bf98afa038b8a89e4ea5..9575af91fa53d3f67925d31e77b81222e737fc3a 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -206,7 +206,7 @@ diff.<driver>.cachetextconv::
 	Set this option to true to make the diff driver cache the text
 	conversion outputs.  See linkgit:gitattributes[5] for details.
 
-include::../mergetools-diff.txt[]
+include::{build_dir}/mergetools-diff.txt[]
 
 diff.indentHeuristic::
 	Set this option to `false` to disable the default heuristics
diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index 8851b6cedef980c106b9e86541862b517aa1f2ad..82554d65a0aa0826fdb53c5d834389d38f308e3e 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -101,7 +101,7 @@ merge.guitool::
 	Any other value is treated as a custom merge tool and requires that a
 	corresponding mergetool.<guitool>.cmd variable is defined.
 
-include::../mergetools-merge.txt[]
+include::{build_dir}/mergetools-merge.txt[]
 
 merge.verbosity::
 	Controls the amount of output shown by the recursive merge
diff --git a/Documentation/git.txt b/Documentation/git.txt
index d15a8697625d6b6c598a2bc774bf243675ab68ea..44f0797ccffb1a7359bee9c20182953293b70d6d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -245,17 +245,17 @@ ancillary user utilities.
 Main porcelain commands
 ~~~~~~~~~~~~~~~~~~~~~~~
 
-include::cmds-mainporcelain.txt[]
+include::{build_dir}/cmds-mainporcelain.txt[]
 
 Ancillary Commands
 ~~~~~~~~~~~~~~~~~~
 Manipulators:
 
-include::cmds-ancillarymanipulators.txt[]
+include::{build_dir}/cmds-ancillarymanipulators.txt[]
 
 Interrogators:
 
-include::cmds-ancillaryinterrogators.txt[]
+include::{build_dir}/cmds-ancillaryinterrogators.txt[]
 
 
 Interacting with Others
@@ -264,7 +264,7 @@ Interacting with Others
 These commands are to interact with foreign SCM and with other
 people via patch over e-mail.
 
-include::cmds-foreignscminterface.txt[]
+include::{build_dir}/cmds-foreignscminterface.txt[]
 
 Reset, restore and revert
 ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -313,13 +313,13 @@ repositories.
 Manipulation commands
 ~~~~~~~~~~~~~~~~~~~~~
 
-include::cmds-plumbingmanipulators.txt[]
+include::{build_dir}/cmds-plumbingmanipulators.txt[]
 
 
 Interrogation commands
 ~~~~~~~~~~~~~~~~~~~~~~
 
-include::cmds-plumbinginterrogators.txt[]
+include::{build_dir}/cmds-plumbinginterrogators.txt[]
 
 In general, the interrogate commands do not touch the files in
 the working tree.
@@ -328,12 +328,12 @@ the working tree.
 Syncing repositories
 ~~~~~~~~~~~~~~~~~~~~
 
-include::cmds-synchingrepositories.txt[]
+include::{build_dir}/cmds-synchingrepositories.txt[]
 
 The following are helper commands used by the above; end users
 typically do not use them directly.
 
-include::cmds-synchelpers.txt[]
+include::{build_dir}/cmds-synchelpers.txt[]
 
 
 Internal helper commands
@@ -342,14 +342,14 @@ Internal helper commands
 These are internal helper commands used by other commands; end
 users typically do not use them directly.
 
-include::cmds-purehelpers.txt[]
+include::{build_dir}/cmds-purehelpers.txt[]
 
 Guides
 ------
 
 The following documentation pages are guides about Git concepts.
 
-include::cmds-guide.txt[]
+include::{build_dir}/cmds-guide.txt[]
 
 Repository, command and file interfaces
 ---------------------------------------
@@ -358,7 +358,7 @@ This documentation discusses repository and command interfaces which
 users are expected to interact with directly. See `--user-formats` in
 linkgit:git-help[1] for more details on the criteria.
 
-include::cmds-userinterfaces.txt[]
+include::{build_dir}/cmds-userinterfaces.txt[]
 
 File formats, protocols and other developer interfaces
 ------------------------------------------------------
@@ -367,7 +367,7 @@ This documentation discusses file formats, over-the-wire protocols and
 other git developer interfaces. See `--developer-interfaces` in
 linkgit:git-help[1].
 
-include::cmds-developerinterfaces.txt[]
+include::{build_dir}/cmds-developerinterfaces.txt[]
 
 Configuration Mechanism
 -----------------------
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 17/22] Documentation: extract script to generate a list of mergetools

We include the list of available mergetools into our manpages. Extract the script that performs this logic such that we can reuse it in other build systems.

While at it, refactor the Makefile targets such that we don't create "mergetools-list.made" anymore. It shouldn't be necessary, as we can instead have other targets depend on "mergetools-{diff,merge}.txt" directly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/Makefile                   | 22 ++++++++--------------
 Documentation/generate-mergetool-list.sh | 17 +++++++++++++++++
 2 files changed, 25 insertions(+), 14 deletions(-)
Show changes to 2 files +25 −14

Documentation/Makefile, Documentation/generate-mergetool-list.sh

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 2b9fd37ff7021f3d44eaa282b942c623f403510a..e2ce98a751f090af8a8c095e7b138ef601e9c0be 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -276,11 +276,13 @@ ifneq ($(filter-out lint-docs clean,$(MAKECMDGOALS)),)
 -include ../GIT-VERSION-FILE
 endif
 
+mergetools_txt = mergetools-diff.txt mergetools-merge.txt
+
 #
 # Determine "include::" file references in asciidoc files.
 #
 docdep_prereqs = \
-	mergetools-list.made $(mergetools_txt) \
+	$(mergetools_txt) \
 	cmd-list.made $(cmds_txt)
 
 doc.dep : $(docdep_prereqs) $(DOC_DEP_TXT) build-docdep.perl
@@ -309,19 +311,11 @@ cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
 	$(QUIET_GEN)$(PERL_PATH) ./cmd-list.perl .. . $(cmds_txt) && \
 	date >$@
 
-mergetools_txt = mergetools-diff.txt mergetools-merge.txt
-
-$(mergetools_txt): mergetools-list.made
-
-mergetools-list.made: ../git-mergetool--lib.sh $(wildcard ../mergetools/*)
-	$(QUIET_GEN) \
-	$(SHELL_PATH) -c 'MERGE_TOOLS_DIR=../mergetools && TOOL_MODE=diff && \
-		. ../git-mergetool--lib.sh && \
-		show_tool_names can_diff' | sed -e "s/\([a-z0-9]*\)/\`\1\`;;/" >mergetools-diff.txt && \
-	$(SHELL_PATH) -c 'MERGE_TOOLS_DIR=../mergetools && TOOL_MODE=merge && \
-		. ../git-mergetool--lib.sh && \
-		show_tool_names can_merge' | sed -e "s/\([a-z0-9]*\)/\`\1\`;;/" >mergetools-merge.txt && \
-	date >$@
+mergetools-%.txt: generate-mergetool-list.sh ../git-mergetool--lib.sh $(wildcard ../mergetools/*)
+mergetools-diff.txt:
+	$(QUIET_GEN)$(SHELL_PATH) ./generate-mergetool-list.sh .. diff $@
+mergetools-merge.txt:
+	$(QUIET_GEN)$(SHELL_PATH) ./generate-mergetool-list.sh .. merge $@
 
 TRACK_ASCIIDOCFLAGS = $(subst ','\'',$(ASCIIDOC_COMMON):$(ASCIIDOC_HTML):$(ASCIIDOC_DOCBOOK))
 
diff --git a/Documentation/generate-mergetool-list.sh b/Documentation/generate-mergetool-list.sh
new file mode 100755
index 0000000000000000000000000000000000000000..696196fbcb86084abffc1d29355705a37b1532b3
--- /dev/null
+++ b/Documentation/generate-mergetool-list.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+if test "$#" -ne 3
+then
+	echo "USAGE: $0 <SOURCE_DIR> <MODE> <OUTPUT>" >&2
+	exit 1
+fi
+
+SOURCE_DIR="$1"
+TOOL_MODE="$2"
+OUTPUT="$3"
+MERGE_TOOLS_DIR="$SOURCE_DIR/mergetools"
+
+(
+	. "$SOURCE_DIR"/git-mergetool--lib.sh &&
+	show_tool_names can_$TOOL_MODE
+) | sed -e "s/\([a-z0-9]*\)/\`\1\`;;/" >"$OUTPUT"
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 19/22] t: allow overriding build dir

Our "test-lib.sh" assumes that our build directory is the parent directory of "t/". While true when using our Makefile, it's not when using build systems that support out-of-tree builds.

In commit ee9e66e4e7 (cmake: avoid editing t/test-lib.sh, 2022-10-18), we have introduce support for overriding the GIT_BUILD_DIR by creating the file "$GIT_BUILD_DIR/GIT-BUILD-DIR" with its contents pointing to the location of the build directory. The intent was to stop modifying "t/test-lib.sh" with the CMake build systems while allowing out-of-tree builds. But "$GIT_BUILD_DIR" is somewhat misleadingly named, as it in fact points to the _source_ directory. So while that commit solved part of the problem for out-of-tree builds, CMake still has to write files into the source tree.

Solve the second part of the problem, namely not having to write any data into the source directory at all, by also supporting an environment variable that allows us to point to a different build directory. This allows us to perform properly self-contained out-of-tree builds.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 t/test-lib.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
Show changes to t/test-lib.sh +2 −1
diff --git a/t/test-lib.sh b/t/test-lib.sh
index ceb926c4258db853eeb50b2259a7f521501cf719..cce0069287af48a607bc24fa474006a47014ea49 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -35,7 +35,7 @@ else
 	# needing to exist.
 	TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1
 fi
-GIT_BUILD_DIR="${TEST_DIRECTORY%/t}"
+GIT_BUILD_DIR="${GIT_BUILD_DIR:-${TEST_DIRECTORY%/t}}"
 if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR"
 then
 	echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2
@@ -522,6 +522,7 @@ unset VISUAL EMAIL LANGUAGE $("$PERL_PATH" -e '
 		PERF_
 		CURL_VERBOSE
 		TRACE_CURL
+		BUILD_DIR
 	));
 	my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
 	print join("\n", @vars);
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 18/22] t: better support for out-of-tree builds

Our in-tree builds used by the Makefile use various different build directories scattered around different locations. The paths to those build directories have to be propagated to our tests such that they can find the contained files. This is done via a mixture of hardcoded paths in our test library and injected variables in our bin-wrappers or "GIT-BUILD-OPTIONS".

The latter two mechanisms are preferable over using hardcoded paths. For one, we have all paths which are subject to change stored in a small set of central files instead of having the knowledge of build paths in many files. And second, it allows build systems which build files elsewhere to adapt those paths based on their own needs. This is especially nice in the context of build systems that use out-of-tree builds like CMake or Meson.

Remove hardcoded knowledge of build paths from our test library and move it into our bin-wrappers and "GIT-BUILD-OPTIONS".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 GIT-BUILD-OPTIONS.in                |  5 +++++
 Makefile                            |  9 +++++++++
 bin-wrappers/wrap-for-bin.sh        | 11 ++++++-----
 contrib/buildsystems/CMakeLists.txt |  8 ++++++++
 t/lib-gettext.sh                    |  4 ++--
 t/t7609-mergetool--lib.sh           |  2 +-
 t/test-lib.sh                       |  6 +++---
 7 files changed, 34 insertions(+), 11 deletions(-)
Show changes to 7 files +34 −11

GIT-BUILD-OPTIONS.in, Makefile, bin-wrappers/wrap-for-bin.sh, contrib/buildsystems/CMakeLists.txt, t/lib-gettext.sh, t/t7609-mergetool--lib.sh, t/test-lib.sh

diff --git a/GIT-BUILD-OPTIONS.in b/GIT-BUILD-OPTIONS.in
index 9b95a6b3eee334b6f691702696ca9d92201b6bac..f651116102ae2977622dccd12b199fe7ad65af99 100644
--- a/GIT-BUILD-OPTIONS.in
+++ b/GIT-BUILD-OPTIONS.in
@@ -35,6 +35,11 @@ GIT_PERF_MAKE_COMMAND=@GIT_PERF_MAKE_COMMAND@
 GIT_INTEROP_MAKE_OPTS=@GIT_INTEROP_MAKE_OPTS@
 GIT_TEST_INDEX_VERSION=@GIT_TEST_INDEX_VERSION@
 GIT_TEST_PERL_FATAL_WARNINGS=@GIT_TEST_PERL_FATAL_WARNINGS@
+GIT_TEST_TEXTDOMAINDIR=@GIT_TEST_TEXTDOMAINDIR@
+GIT_TEST_POPATH=@GIT_TEST_POPATH@
+GIT_TEST_TEMPLATE_DIR=@GIT_TEST_TEMPLATE_DIR@
+GIT_TEST_GITPERLLIB=@GIT_TEST_GITPERLLIB@
+GIT_TEST_MERGE_TOOLS_DIR=@GIT_TEST_MERGE_TOOLS_DIR@
 RUNTIME_PREFIX=@RUNTIME_PREFIX@
 GITWEBDIR=@GITWEBDIR@
 USE_GETTEXT_SCHEME=@USE_GETTEXT_SCHEME@
diff --git a/Makefile b/Makefile
index 056234e0bc0722ebc9f5d6ccae97f98fc9a64a37..26845907657ae7d87530dc8d3f51761127afa95f 100644
--- a/Makefile
+++ b/Makefile
@@ -3174,6 +3174,11 @@ GIT-BUILD-OPTIONS: FORCE
 		-e "s|@GIT_INTEROP_MAKE_OPTS@|\'$(GIT_INTEROP_MAKE_OPTS)\'|" \
 		-e "s|@GIT_TEST_INDEX_VERSION@|\'$(GIT_TEST_INDEX_VERSION)\'|" \
 		-e "s|@GIT_TEST_PERL_FATAL_WARNINGS@|\'$(GIT_TEST_PERL_FATAL_WARNINGS)\'|" \
+		-e "s|@GIT_TEST_TEXTDOMAINDIR@|\'$(shell pwd)/po/build/locale\'|" \
+		-e "s|@GIT_TEST_POPATH@|\'$(shell pwd)/po\'|" \
+		-e "s|@GIT_TEST_TEMPLATE_DIR@|\'$(shell pwd)/templates/blt\'|" \
+		-e "s|@GIT_TEST_GITPERLLIB@|\'$(shell pwd)/perl/build/lib\'|" \
+		-e "s|@GIT_TEST_MERGE_TOOLS_DIR@|\'$(shell pwd)/mergetools\'|" \
 		-e "s|@RUNTIME_PREFIX@|\'$(RUNTIME_PREFIX)\'|" \
 		-e "s|@GITWEBDIR@|\'$(gitwebdir_SQ)\'|" \
 		-e "s|@USE_GETTEXT_SCHEME@|\'$(USE_GETTEXT_SCHEME)\'|" \
@@ -3203,6 +3208,10 @@ all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(CLAR_TEST_PR
 $(test_bindir_programs): bin-wrappers/%: bin-wrappers/wrap-for-bin.sh
 	$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 	     -e 's|@BUILD_DIR@|$(shell pwd)|' \
+	     -e 's|@GIT_TEXTDOMAINDIR@|$(shell pwd)/po/build/locale|' \
+	     -e 's|@GITPERLLIB@|$(shell pwd)/perl/build/lib|' \
+	     -e 's|@MERGE_TOOLS_DIR@|$(shell pwd)/mergetools|' \
+	     -e 's|@TEMPLATE_DIR@|$(shell pwd)/templates/blt|' \
 	     -e 's|@PROG@|$(patsubst test-%,t/helper/test-%,$(@F))$(if $(filter-out $(BINDIR_PROGRAMS_NO_X),$(@F)),$(X),)|' < $< > $@ && \
 	chmod +x $@
 
diff --git a/bin-wrappers/wrap-for-bin.sh b/bin-wrappers/wrap-for-bin.sh
index 7898a1c238dde20617cf96d144930dc59f67feab..1d3a59a0081e1fc6066af187093cb1d11d01a929 100755
--- a/bin-wrappers/wrap-for-bin.sh
+++ b/bin-wrappers/wrap-for-bin.sh
@@ -4,21 +4,22 @@
 # to run test suite against sandbox, but with only bindir-installed
 # executables in PATH.  The Makefile copies this into various
 # files in bin-wrappers, substituting
-# @BUILD_DIR@ and @PROG@.
+# @BUILD_DIR@, @TEMPLATE_DIR@ and @PROG@.
 
 GIT_EXEC_PATH='@BUILD_DIR@'
 if test -n "$NO_SET_GIT_TEMPLATE_DIR"
 then
 	unset GIT_TEMPLATE_DIR
 else
-	GIT_TEMPLATE_DIR='@BUILD_DIR@/templates/blt'
+	GIT_TEMPLATE_DIR='@TEMPLATE_DIR@'
 	export GIT_TEMPLATE_DIR
 fi
-GITPERLLIB='@BUILD_DIR@/perl/build/lib'"${GITPERLLIB:+:$GITPERLLIB}"
-GIT_TEXTDOMAINDIR='@BUILD_DIR@/po/build/locale'
+MERGE_TOOLS_DIR='@MERGE_TOOLS_DIR@'
+GITPERLLIB='@GITPERLLIB@'"${GITPERLLIB:+:$GITPERLLIB}"
+GIT_TEXTDOMAINDIR='@GIT_TEXTDOMAINDIR@'
 PATH='@BUILD_DIR@/bin-wrappers:'"$PATH"
 
-export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
+export MERGE_TOOLS_DIR GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
 
 case "$GIT_DEBUGGER" in
 '')
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 852b841601642402fb47489fba07445435e27930..25d8452cc29ecf2ee21172bc8a31f57801908ba7 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1085,6 +1085,9 @@ endforeach()
 
 file(STRINGS ${CMAKE_SOURCE_DIR}/bin-wrappers/wrap-for-bin.sh content NEWLINE_CONSUME)
 string(REPLACE "@BUILD_DIR@" "${CMAKE_BINARY_DIR}" content "${content}")
+string(REPLACE "@GIT_TEXTDOMAINDIR@" "${CMAKE_BINARY_DIR}/po/build/locale" content "${content}")
+string(REPLACE "@GITPERLLIB@" "${CMAKE_BINARY_DIR}/perl/build/lib" content "${content}")
+string(REPLACE "@MERGE_TOOLS_DIR@" "${CMAKE_SOURCE_DIR}/mergetools" content "${content}")
 string(REPLACE "@PROG@" "git-cvsserver" content "${content}")
 file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})
 
@@ -1170,6 +1173,11 @@ string(REPLACE "@GIT_PERF_MAKE_COMMAND@" "" git_build_options "${git_build_optio
 string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
 string(REPLACE "@GIT_TEST_INDEX_VERSION@" "" git_build_options "${git_build_options}")
 string(REPLACE "@GIT_TEST_PERL_FATAL_WARNINGS@" "" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_TEXTDOMAINDIR@" "'${CMAKE_BINARY_DIR}/po/build/locale'" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_POPATH@" "'${CMAKE_BINARY_DIR}/po'" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_TEMPLATE_DIR@" "'${CMAKE_BINARY_DIR}/templates/blt'" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_GITPERLLIB@" "'${CMAKE_BINARY_DIR}/perl/build/lib'" git_build_options "${git_build_options}")
+string(REPLACE "@GIT_TEST_MERGE_TOOLS_DIR@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
 string(REPLACE "@RUNTIME_PREFIX@" "'${RUNTIME_PREFIX}'" git_build_options "${git_build_options}")
 string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
 string(REPLACE "@USE_GETTEXT_SCHEME@" "" git_build_options "${git_build_options}")
diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh
index cc6bb2cdeaae7b5bb23211c0d9c37c9deb39e14f..7a734c6973e65995d3403a26cc9e5c1a5a1ce074 100644
--- a/t/lib-gettext.sh
+++ b/t/lib-gettext.sh
@@ -6,8 +6,8 @@
 
 . ./test-lib.sh
 
-GIT_TEXTDOMAINDIR="$GIT_BUILD_DIR/po/build/locale"
-GIT_PO_PATH="$GIT_BUILD_DIR/po"
+GIT_TEXTDOMAINDIR="$GIT_TEST_TEXTDOMAINDIR"
+GIT_PO_PATH="$GIT_TEST_POPATH"
 export GIT_TEXTDOMAINDIR GIT_PO_PATH
 
 if test -n "$GIT_TEST_INSTALLED"
diff --git a/t/t7609-mergetool--lib.sh b/t/t7609-mergetool--lib.sh
index 8b1c3bd39f2249417099ea2f24218268925909af..a9273ba58d76333c74d0072794ed0d95b966f16f 100755
--- a/t/t7609-mergetool--lib.sh
+++ b/t/t7609-mergetool--lib.sh
@@ -8,7 +8,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'mergetool --tool=vimdiff creates the expected layout' '
-	. "$GIT_BUILD_DIR"/mergetools/vimdiff &&
+	. "$GIT_TEST_MERGE_TOOLS_DIR"/vimdiff &&
 	run_unit_tests
 '
 
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 19057faa73925f619750fec30f05c3e5a1ba05ac..ceb926c4258db853eeb50b2259a7f521501cf719 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1487,7 +1487,7 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
 		PATH="$GIT_BUILD_DIR:$GIT_BUILD_DIR/t/helper:$PATH"
 	fi
 fi
-GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
+GIT_TEMPLATE_DIR="$GIT_TEST_TEMPLATE_DIR"
 GIT_CONFIG_NOSYSTEM=1
 GIT_ATTR_NOSYSTEM=1
 GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/.."
@@ -1503,9 +1503,9 @@ then
 	fi
 fi
 
-GITPERLLIB="$GIT_BUILD_DIR"/perl/build/lib
+GITPERLLIB="$GIT_TEST_GITPERLLIB"
 export GITPERLLIB
-test -d "$GIT_BUILD_DIR"/templates/blt || {
+test -d "$GIT_TEMPLATE_DIR" || {
 	BAIL_OUT "You haven't built things yet, have you?"
 }
 
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 20/22] Documentation: add comparison of build systems

We're contemplating whether to eventually replace our build systems with a build system that is easier to use. Add a comparison of build systems to our technical documentation as a baseline for discussion.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/Makefile                    |   1 +
 Documentation/technical/build-systems.txt | 224 ++++++++++++++++++++++++++++++
 2 files changed, 225 insertions(+)
Show changes to 2 files +225 −0

Documentation/Makefile, Documentation/technical/build-systems.txt

diff --git a/Documentation/Makefile b/Documentation/Makefile
index e2ce98a751f090af8a8c095e7b138ef601e9c0be..e1527c6d4429efdd2341163ef895a13ddeae8e2f 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -111,6 +111,7 @@ TECH_DOCS += MyFirstObjectWalk
 TECH_DOCS += SubmittingPatches
 TECH_DOCS += ToolsForGit
 TECH_DOCS += technical/bitmap-format
+TECH_DOCS += technical/build-systems
 TECH_DOCS += technical/bundle-uri
 TECH_DOCS += technical/hash-function-transition
 TECH_DOCS += technical/long-running-process-protocol
diff --git a/Documentation/technical/build-systems.txt b/Documentation/technical/build-systems.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d9dafb407c40902756ca0bde9d9dad8ced8788dd
--- /dev/null
+++ b/Documentation/technical/build-systems.txt
@@ -0,0 +1,224 @@
+= Build Systems
+
+The build system is the primary way for both developers and system integrators
+to interact with the Git project. As such, being easy to use and extend for
+those who are not directly developing Git itself is just as important as other
+requirements we have on any potential build system.
+
+This document outlines the different requirements that we have for the build
+system and then compares available build systems using these criteria.
+
+== Requirements
+
+The following subsections present a list of requirements that we have for any
+potential build system. Sections are sorted by decreasing priority.
+
+=== Platform support
+
+The build system must have support for all of our platforms that we continually
+test against as outlined by our platform support policy. These platforms are:
+
+  - Linux
+  - Windows
+  - macOS
+
+Furthermore, the build system should have support for the following platforms
+that generally have somebody running test pipelines against regularly:
+
+  - AIX
+  - FreeBSD
+  - NetBSD
+  - NonStop
+  - OpenBSD
+
+The platforms which must be supported by the tool should be aligned with our
+[platform support policy](platform-support.txt).
+
+=== Auto-detection of supported features
+
+The build system must support auto-detection of features which are or aren't
+available on the current platform. Platform maintainers should not be required
+to manually configure the complete build.
+
+Auto-detection of the following items is considered to be important:
+
+  - Check for the existence of headers.
+  - Check for the existence of libraries.
+  - Check for the existence of exectuables.
+  - Check for the runtime behavior of specific functions.
+  - Check for specific link order requirements when multiple libraries are
+    involved.
+
+=== Ease of use
+
+The build system should be both easy to use and easy to extend. While this is
+naturally a subjective metric it is likely not controversial to say that some
+build systems are considerably harder to use than others.
+
+=== IDE support
+
+The build system should integrate with well-known IDEs. Well-known IDEs include:
+
+  - Microsoft Visual Studio
+  - Visual Studio Code
+  - Xcode
+
+There are four levels of support:
+
+  - Native integration into the IDE.
+  - Integration into the IDE via a plugin.
+  - Integration into the IDE via generating a project description with the build
+    system.
+  - No integration.
+
+Native integration is preferable, but integration via either a plugin or by
+generating a project description via the build system are considered feasible
+alternatives.
+
+Another important distinction is the level of integration. There are two
+features that one generally wants to have:
+
+  - Integration of build targets.
+  - Automatic setup of features like code completion with detected build
+    dependencies.
+
+The first bullet point is the bare minimum, but is not sufficient to be
+considered proper integration.
+
+=== Out-of-tree builds
+
+The build system should support out-of-tree builds. Out-of-tree builds allow a
+developer to configure multiple different build directories with different
+configuration, e.g. one "debug" build and one "release" build.
+
+=== Cross-platform builds
+
+The build system should support cross-platform builds, e.g. building for arm on
+an x86-64 host.
+
+=== Language support
+
+The following languages and toolchains are of relevance and should be supported
+by the build system:
+
+  - C: the primary compiled language used by Git, must be supported. Relevant
+    toolchains are GCC, Clang and MSVC.
+  - Rust: candidate as a second compiled lanugage, should be supported. Relevant
+    toolchains is the LLVM-based rustc.
+
+Built-in support for the respective languages is preferred over support that
+needs to be wired up manually to avoid unnecessary complexity. Native support
+includes the following features:
+
+  - Compiling objects.
+  - Dependency tracking.
+  - Detection of available features.
+  - Discovery of relevant toolchains.
+  - Linking libraries and executables.
+  - Templating placeholders in scripts.
+
+=== Test integration
+
+It should be possible to integrate tests into the build system such that it is
+possible to build and test Git within the build system. Features which are nice
+to have:
+
+  - Track build-time dependencies for respective tests. Unit tests have
+    different requirements than integration tests.
+  - Allow filtering of which tests to run.
+  - Allow running tests such that utilities like `test_pause` or `debug` work.
+
+== Comparison
+
+The following list of build systems are considered:
+
+- GNU Make
+- autoconf
+- CMake
+- Meson
+
+=== GNU Make
+
+- Platform support: ubitquitous on all platforms, but not well-integrated into Windows.
+- Auto-detection: no built-in support for auto-detection of features.
+- Ease of use: easy to use, but discovering available options is hard. Makefile
+  rules can quickly get out of hand once reaching a certain scope.
+- IDE support: execution of Makefile targets is supported by many IDEs
+- Out-of-tree builds: supported in theory, not wired up in practice.
+- Cross-platform builds: supported in theory, not wired up in practice.
+- Language support:
+  - C: Limited built-in support, many parts need to be wired up manually.
+  - Rust: No built-in support, needs to be wired up manually.
+- Test integration: partially supported, many parts need to be wired up
+  manually.
+
+=== autoconf
+
+- Platform support: ubiquitous on all platforms, but not well-integrated into Windows.
+- Auto-detection: supported.
+- Ease of use: easy to use, discovering available options is comparatively
+  easy. The autoconf syntax is prohibitively hard to extend though due to its
+  complex set of interacting files and the hard-to-understand M4 language.
+- IDE support: no integration into IDEs at generation time. The generated
+  Makefiles have the same level of support as GNU Make.
+- Out-of-tree builds: supported in theory, not wired up in practice.
+- Cross-platform builds: supported.
+- Language support:
+  - C: Limited built-in support, many parts need to be wired up manually.
+  - Rust: No built-in support, needs to be wired up manually.
+- Test integration: partially supported, many parts need to be wired up
+  manually.
+
+=== CMake
+
+- Platform support: not as extensive as GNU Make or autoconf, but all major
+  platforms are supported.
+  - AIX
+  - Cygwin
+  - FreeBSD
+  - Linux
+  - OpenBSD
+  - Solaris
+  - Windows
+  - macOS
+- Ease of use: easy to use, discovering available options is not always
+  trivial. The scripting language used by CMake is somewhat cumbersome to use,
+  but extending CMake build instructions is doable.
+- IDE support: natively integrated into Microsoft Visual Studio. Can generate
+  project descriptions for Xcode. An extension is available for Visual Studio
+  Code. Many other IDEs have plugins for CMake.
+- Out-of-tree builds: supported.
+- Cross-platform builds: supported.
+- Language support:
+  - C: Supported for GCC, Clang, MSVC and other toolchains.
+  - Rust: No built-in support, needs to be wired up manually.
+- Test integration: supported, even though test dependencies are a bit
+  cumbersome to use via "test fixtures". Interactive test runs are not
+  supported.
+
+=== Meson
+
+- Platform: not as extensive as GNU Make or autoconf, but all major platforms
+  and some smaller ones are supported.
+  - AIX
+  - Cygwin
+  - DragonflyBSD
+  - FreeBSD
+  - Haiku
+  - Linux
+  - NetBSD
+  - OpenBSD
+  - Solaris
+  - Windows
+  - macOS
+- Ease of use: easy to use, discovering available options is easy. The
+  scripting language is straight-forward to use.
+- IDE support: Supports generating build instructions for Xcode and Microsoft
+  Visual Studio, a plugin exists for Visual Studio Code.
+- Out-of-tree builds: supported.
+- Cross-platform builds: supported.
+- Language support:
+  - C: Supported for GCC, Clang, MSVC and other toolchains.
+  - Rust: Supported for rustc.
+- Test integration: supported. Interactive tests are supported starting with
+  Meson 1.5.0 via the `--interactive` flag.
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 21/22] Introduce support for the Meson build system

Introduce support for the Meson build system, a "modern" meta build system that supports many different platforms, including Linux, macOS, Windows and BSDs. Meson supports different backends, including Ninja, Xcode and Microsoft Visual Studio. Several common IDEs provide an integration with it.

The biggest contender compared to Meson is probably CMake as outlined in our "Documentation/technical/build-systems.txt" file. Based on my own personal experience from working with both build systems extensively I strongly favor Meson over CMake. In my opinion, it feels significantly easier to use with a syntax that feels more like a "real" programming language. The second big reason is that Meson supports Rust natively, which may prove to be important given that the project may pick up Rust as another language eventually.

Using Meson is rather straight-forward. An example:
    ```
    # Meson uses out-of-tree builds. You can set up multiple build
    # directories, how you name them is completely up to you.
    $ mkdir build
    $ cd build
    $ meson setup .. -Dprefix=/tmp/git-installation
    # Build the project. This also provides several other targets like
    e.g. `install` or `test`.
    $ ninja
    # Meson has been wired up to support execution of our test suites.
    # Both our unit tests and our integration tests are supported.
    # Running `meson test` without any arguments will execute all tests,
    # but the syntax supports globbing to select only some tests.
    $ meson test 't-*'
    # Execute single test interactively to allow for debugging.
    $ meson test 't0000-*' --interactive --test-args=-ix
    ```

The build instructions have been successfully tested on the following systems, tests are passing:

  - Apple macOS 10.15.
  - FreeBSD 14.1.
  - NixOS 24.11.
  - OpenBSD 7.6.
  - Ubuntu 24.04.
  - Windows 10 with Cygwin.
  - Windows 10 with MinGW64, except for t9700, which is also broken with
    our Makefile.
  - Windows 10 with Visual Studio 2022 toolchain, using the Native Tools
    Command Prompt with `meson setup --vsenv`. Tests pass, except for
    t9700.
  - Windows 10 with Visual Studio 2022 solution, using the Native Tools
    Command Prompt with `meson setup --backend vs2022`. Tests pass,
    except for t9700.
  - Windows 10 with VS Code, using the Meson plug-in.

It is expected that there will still be rough edges in the current version. If this patch lands the expectation is that it will coexist with our other build systems for a while. Like this, distributions can slowly migrate over to Meson and report any findings they have to us such that we can continue to iterate. A potential cutoff date for other build systems may be Git 3.0.

Some notes:
  - The installed distribution is structured somewhat differently than
    how it used to be the case. All of our binaries are installed into
    `$libexec/git-core`, while all binaries part of `$bindir` are now
    symbolic links pointing to the former. This rule is consistent in
    itself and thus easier to reason about.
  - We do not install dashed binaries into `$libexec/git-core` anymore,
    so there won't e.g. be a symlink for git-add(1). These are not
    required by modern Git and there isn't really much of a use case for
    those anymore. By not installing those symlinks we thus start the
    deprecation of this layout.
  - We're targeting Meson 1.3.0, which has been released relatively
    recently November 2023. The only feature we use from that version is
    `fs.relative_to()`, which we could replace if necessary. If so, we
    could start to target Meson 1.0.0 and newer, released in December
    2022.
  - The whole build instructions count around 3300 lines, half of which
    is listing all of our code and test files. Our Makefiles are around
    5000 lines, autoconf adds another 1300 lines. CMake in comparison
    has only 1200 linescode, but it avoids listing individual files and
    does not wire up auto-configuration as extensively as the Meson
    instructions do.
  - We bundle a set of subproject wrappers for curl, expat, openssl,
    pcre2 and zlib. This allows developers to build Git without these
    dependencies preinstalled, and Meson will fetch and build them
    automatically. This is especially helpful on Windows.
Helped-by: Eli Schwartz <eschwartz@gentoo.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/meson.build          |  317 ++++++
 bin-wrappers/meson.build           |   28 +
 contrib/completion/meson.build     |    8 +
 contrib/meson.build                |    1 +
 gitweb/meson.build                 |   79 ++
 meson.build                        | 1852 ++++++++++++++++++++++++++++++++++++
 meson_options.txt                  |   83 ++
 perl/FromCPAN/Mail/meson.build     |    7 +
 perl/FromCPAN/meson.build          |    9 +
 perl/Git/LoadCPAN/Mail/meson.build |    7 +
 perl/Git/LoadCPAN/meson.build      |    9 +
 perl/Git/SVN/Memoize/meson.build   |    7 +
 perl/Git/SVN/meson.build           |   20 +
 perl/Git/meson.build               |   18 +
 perl/meson.build                   |   12 +
 po/meson.build                     |   27 +
 subprojects/.gitignore             |    1 +
 subprojects/curl.wrap              |   13 +
 subprojects/expat.wrap             |   13 +
 subprojects/openssl.wrap           |   15 +
 subprojects/pcre2.wrap             |   16 +
 subprojects/zlib.wrap              |   13 +
 t/helper/meson.build               |   91 ++
 t/meson.build                      | 1106 +++++++++++++++++++++
 templates/hooks/meson.build        |   26 +
 templates/info/meson.build         |    7 +
 templates/meson.build              |   15 +
 27 files changed, 3800 insertions(+)
Show changes to 27 files +3800 −0

Documentation/meson.build, bin-wrappers/meson.build, contrib/completion/meson.build, contrib/meson.build, gitweb/meson.build, meson.build, meson_options.txt, perl/FromCPAN/Mail/meson.build, perl/FromCPAN/meson.build, perl/Git/LoadCPAN/Mail/meson.build, perl/Git/LoadCPAN/meson.build, perl/Git/SVN/Memoize/meson.build, perl/Git/SVN/meson.build, perl/Git/meson.build, perl/meson.build, po/meson.build, subprojects/.gitignore, subprojects/curl.wrap, subprojects/expat.wrap, subprojects/openssl.wrap, subprojects/pcre2.wrap, subprojects/zlib.wrap, t/helper/meson.build, t/meson.build, templates/hooks/meson.build, templates/info/meson.build, templates/meson.build

diff --git a/Documentation/meson.build b/Documentation/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..15ba5004ad43188fc9ff9b85148278b20c03bf6f
--- /dev/null
+++ b/Documentation/meson.build
@@ -0,0 +1,317 @@
+manpages = {
+  # Category 1.
+  'git-add.txt' : 1,
+  'git-am.txt' : 1,
+  'git-annotate.txt' : 1,
+  'git-apply.txt' : 1,
+  'git-archimport.txt' : 1,
+  'git-archive.txt' : 1,
+  'git-bisect.txt' : 1,
+  'git-blame.txt' : 1,
+  'git-branch.txt' : 1,
+  'git-bugreport.txt' : 1,
+  'git-bundle.txt' : 1,
+  'git-cat-file.txt' : 1,
+  'git-check-attr.txt' : 1,
+  'git-check-ignore.txt' : 1,
+  'git-check-mailmap.txt' : 1,
+  'git-checkout-index.txt' : 1,
+  'git-checkout.txt' : 1,
+  'git-check-ref-format.txt' : 1,
+  'git-cherry-pick.txt' : 1,
+  'git-cherry.txt' : 1,
+  'git-citool.txt' : 1,
+  'git-clean.txt' : 1,
+  'git-clone.txt' : 1,
+  'git-column.txt' : 1,
+  'git-commit-graph.txt' : 1,
+  'git-commit-tree.txt' : 1,
+  'git-commit.txt' : 1,
+  'git-config.txt' : 1,
+  'git-count-objects.txt' : 1,
+  'git-credential-cache--daemon.txt' : 1,
+  'git-credential-cache.txt' : 1,
+  'git-credential-store.txt' : 1,
+  'git-credential.txt' : 1,
+  'git-cvsexportcommit.txt' : 1,
+  'git-cvsimport.txt' : 1,
+  'git-cvsserver.txt' : 1,
+  'git-daemon.txt' : 1,
+  'git-describe.txt' : 1,
+  'git-diagnose.txt' : 1,
+  'git-diff-files.txt' : 1,
+  'git-diff-index.txt' : 1,
+  'git-difftool.txt' : 1,
+  'git-diff-tree.txt' : 1,
+  'git-diff.txt' : 1,
+  'git-fast-export.txt' : 1,
+  'git-fast-import.txt' : 1,
+  'git-fetch-pack.txt' : 1,
+  'git-fetch.txt' : 1,
+  'git-filter-branch.txt' : 1,
+  'git-fmt-merge-msg.txt' : 1,
+  'git-for-each-ref.txt' : 1,
+  'git-for-each-repo.txt' : 1,
+  'git-format-patch.txt' : 1,
+  'git-fsck-objects.txt' : 1,
+  'git-fsck.txt' : 1,
+  'git-fsmonitor--daemon.txt' : 1,
+  'git-gc.txt' : 1,
+  'git-get-tar-commit-id.txt' : 1,
+  'git-grep.txt' : 1,
+  'git-gui.txt' : 1,
+  'git-hash-object.txt' : 1,
+  'git-help.txt' : 1,
+  'git-hook.txt' : 1,
+  'git-http-backend.txt' : 1,
+  'git-http-fetch.txt' : 1,
+  'git-http-push.txt' : 1,
+  'git-imap-send.txt' : 1,
+  'git-index-pack.txt' : 1,
+  'git-init-db.txt' : 1,
+  'git-init.txt' : 1,
+  'git-instaweb.txt' : 1,
+  'git-interpret-trailers.txt' : 1,
+  'git-log.txt' : 1,
+  'git-ls-files.txt' : 1,
+  'git-ls-remote.txt' : 1,
+  'git-ls-tree.txt' : 1,
+  'git-mailinfo.txt' : 1,
+  'git-mailsplit.txt' : 1,
+  'git-maintenance.txt' : 1,
+  'git-merge-base.txt' : 1,
+  'git-merge-file.txt' : 1,
+  'git-merge-index.txt' : 1,
+  'git-merge-one-file.txt' : 1,
+  'git-mergetool--lib.txt' : 1,
+  'git-mergetool.txt' : 1,
+  'git-merge-tree.txt' : 1,
+  'git-merge.txt' : 1,
+  'git-mktag.txt' : 1,
+  'git-mktree.txt' : 1,
+  'git-multi-pack-index.txt' : 1,
+  'git-mv.txt' : 1,
+  'git-name-rev.txt' : 1,
+  'git-notes.txt' : 1,
+  'git-p4.txt' : 1,
+  'git-pack-objects.txt' : 1,
+  'git-pack-redundant.txt' : 1,
+  'git-pack-refs.txt' : 1,
+  'git-patch-id.txt' : 1,
+  'git-prune-packed.txt' : 1,
+  'git-prune.txt' : 1,
+  'git-pull.txt' : 1,
+  'git-push.txt' : 1,
+  'git-quiltimport.txt' : 1,
+  'git-range-diff.txt' : 1,
+  'git-read-tree.txt' : 1,
+  'git-rebase.txt' : 1,
+  'git-receive-pack.txt' : 1,
+  'git-reflog.txt' : 1,
+  'git-refs.txt' : 1,
+  'git-remote-ext.txt' : 1,
+  'git-remote-fd.txt' : 1,
+  'git-remote.txt' : 1,
+  'git-repack.txt' : 1,
+  'git-replace.txt' : 1,
+  'git-replay.txt' : 1,
+  'git-request-pull.txt' : 1,
+  'git-rerere.txt' : 1,
+  'git-reset.txt' : 1,
+  'git-restore.txt' : 1,
+  'git-revert.txt' : 1,
+  'git-rev-list.txt' : 1,
+  'git-rev-parse.txt' : 1,
+  'git-rm.txt' : 1,
+  'git-send-email.txt' : 1,
+  'git-send-pack.txt' : 1,
+  'git-shell.txt' : 1,
+  'git-sh-i18n--envsubst.txt' : 1,
+  'git-sh-i18n.txt' : 1,
+  'git-shortlog.txt' : 1,
+  'git-show-branch.txt' : 1,
+  'git-show-index.txt' : 1,
+  'git-show-ref.txt' : 1,
+  'git-show.txt' : 1,
+  'git-sh-setup.txt' : 1,
+  'git-sparse-checkout.txt' : 1,
+  'git-stage.txt' : 1,
+  'git-stash.txt' : 1,
+  'git-status.txt' : 1,
+  'git-stripspace.txt' : 1,
+  'git-submodule.txt' : 1,
+  'git-svn.txt' : 1,
+  'git-switch.txt' : 1,
+  'git-symbolic-ref.txt' : 1,
+  'git-tag.txt' : 1,
+  'git-unpack-file.txt' : 1,
+  'git-unpack-objects.txt' : 1,
+  'git-update-index.txt' : 1,
+  'git-update-ref.txt' : 1,
+  'git-update-server-info.txt' : 1,
+  'git-upload-archive.txt' : 1,
+  'git-upload-pack.txt' : 1,
+  'git-var.txt' : 1,
+  'git-verify-commit.txt' : 1,
+  'git-verify-pack.txt' : 1,
+  'git-verify-tag.txt' : 1,
+  'git-version.txt' : 1,
+  'git-web--browse.txt' : 1,
+  'git-whatchanged.txt' : 1,
+  'git-worktree.txt' : 1,
+  'git-write-tree.txt' : 1,
+  'git.txt' : 1,
+  'gitk.txt' : 1,
+  'gitweb.txt' : 1,
+  'scalar.txt' : 1,
+
+  # Category 5.
+  'gitattributes.txt' : 5,
+  'gitformat-bundle.txt' : 5,
+  'gitformat-chunk.txt' : 5,
+  'gitformat-commit-graph.txt' : 5,
+  'gitformat-index.txt' : 5,
+  'gitformat-pack.txt' : 5,
+  'gitformat-signature.txt' : 5,
+  'githooks.txt' : 5,
+  'gitignore.txt' : 5,
+  'gitmailmap.txt' : 5,
+  'gitmodules.txt' : 5,
+  'gitprotocol-capabilities.txt' : 5,
+  'gitprotocol-common.txt' : 5,
+  'gitprotocol-http.txt' : 5,
+  'gitprotocol-pack.txt' : 5,
+  'gitprotocol-v2.txt' : 5,
+  'gitrepository-layout.txt' : 5,
+  'gitweb.conf.txt' : 5,
+
+  # Category 7.
+  'gitcli.txt' : 7,
+  'gitcore-tutorial.txt' : 7,
+  'gitcredentials.txt' : 7,
+  'gitcvs-migration.txt' : 7,
+  'gitdiffcore.txt' : 7,
+  'giteveryday.txt' : 7,
+  'gitfaq.txt' : 7,
+  'gitglossary.txt' : 7,
+  'gitpacking.txt' : 7,
+  'gitnamespaces.txt' : 7,
+  'gitremote-helpers.txt' : 7,
+  'gitrevisions.txt' : 7,
+  'gitsubmodules.txt' : 7,
+  'gittutorial-2.txt' : 7,
+  'gittutorial.txt' : 7,
+  'gitworkflows.txt' : 7,
+}
+
+asciidoc = find_program('asciidoc')
+git = find_program('git', required: false)
+xmlto = find_program('xmlto')
+
+git_revdate = ''
+if git.found()
+  git_revdate = run_command(git, 'show', '--quiet', '--pretty=%as', check: false).stdout().strip()
+endif
+
+asciidoc_common_options = [
+  asciidoc,
+  '--conf-file=' + meson.current_source_dir() / 'asciidoc.conf',
+  '--attribute=manual=Git Manual',
+  '--attribute=mansource=Git ' + git_version,
+  '--attribute=revdate=' + git_revdate,
+  '--attribute=build_dir=' + meson.current_build_dir(),
+]
+
+cmd_lists = [
+    'cmds-ancillaryinterrogators.txt',
+	'cmds-ancillarymanipulators.txt',
+	'cmds-mainporcelain.txt',
+	'cmds-plumbinginterrogators.txt',
+	'cmds-plumbingmanipulators.txt',
+	'cmds-synchingrepositories.txt',
+	'cmds-synchelpers.txt',
+	'cmds-guide.txt',
+	'cmds-developerinterfaces.txt',
+	'cmds-userinterfaces.txt',
+	'cmds-purehelpers.txt',
+	'cmds-foreignscminterface.txt',
+]
+
+documentation_deps = [ ]
+
+documentation_deps += custom_target(
+  command: [
+    perl,
+    meson.current_source_dir() / 'cmd-list.perl',
+    meson.project_source_root(),
+    meson.current_build_dir(),
+  ] + cmd_lists,
+  output: cmd_lists
+)
+
+foreach mode : [ 'diff', 'merge' ]
+  documentation_deps += custom_target(
+    command: [
+      shell,
+      meson.current_source_dir() / 'generate-mergetool-list.sh',
+      '..',
+      'diff',
+      '@OUTPUT@'
+    ],
+    env: [
+      'MERGE_TOOLS_DIR=' + meson.project_source_root() / 'mergetools',
+      'TOOL_MODE=' + mode,
+    ],
+    output: 'mergetools-' + mode + '.txt',
+  )
+endforeach
+
+foreach manpage, category : manpages
+  if get_option('docs').contains('man')
+    manpage_xml_target = custom_target(
+      command: asciidoc_common_options + [
+        '--backend=docbook',
+        '--doctype=manpage',
+        '--out-file=@OUTPUT@',
+        meson.current_source_dir() / manpage,
+      ],
+      depends: documentation_deps,
+      output: fs.stem(manpage) + '.xml',
+    )
+
+    manpage_path = fs.stem(manpage) + '.' + category.to_string()
+    manpage_target = custom_target(
+      command: [
+        xmlto,
+        '-m',
+        meson.current_source_dir() / 'manpage-normal.xsl',
+        '-m',
+        meson.current_source_dir() / 'manpage-bold-literal.xsl',
+        '--stringparam',
+        'man.base.url.for.relative.links=' + get_option('prefix') / get_option('mandir'),
+        'man',
+        manpage_xml_target,
+        '-o',
+        meson.current_build_dir(),
+      ],
+      output: manpage_path,
+      install: true,
+      install_dir: get_option('mandir') / 'man' + category.to_string(),
+    )
+  endif
+
+  if get_option('docs').contains('html') and category == 1
+    custom_target(
+      command: asciidoc_common_options + [
+        '--backend=xhtml11',
+        '--doctype=manpage',
+        '--out-file=@OUTPUT@',
+        meson.current_source_dir() / manpage,
+      ],
+      depends: documentation_deps,
+      output: fs.stem(manpage) + '.html',
+      install: true,
+      install_dir: get_option('datadir') / 'doc/git-doc',
+    )
+  endif
+endforeach
diff --git a/bin-wrappers/meson.build b/bin-wrappers/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..6d03a19d7b8e3cf78ac0499b6cedf47e0c6cb441
--- /dev/null
+++ b/bin-wrappers/meson.build
@@ -0,0 +1,28 @@
+bin_wrappers_config = configuration_data()
+foreach key, value : {
+      'BUILD_DIR': meson.project_build_root(),
+      'MERGE_TOOLS_DIR': meson.project_source_root() / 'mergetools',
+      'TEMPLATE_DIR': meson.project_build_root() / 'templates',
+      'GIT_TEXTDOMAINDIR': meson.project_build_root() / 'po',
+      'GITPERLLIB': meson.project_build_root() / 'perl/lib',
+}
+  # Paths need to be Unix-style without drive prefixes as they get added to the
+  # PATH variable. And given that drive prefixes contain a colon we'd otherwise
+  # end up with a broken PATH if we didn't convert them.
+  if cygpath.found()
+    value = run_command(cygpath, value, check: true).stdout().strip()
+  endif
+  bin_wrappers_config.set(key, value)
+endforeach
+
+foreach executable : bin_wrappers
+  executable_config = configuration_data()
+  executable_config.merge_from(bin_wrappers_config)
+  executable_config.set('PROG', fs.relative_to(executable.full_path(), meson.project_build_root()))
+
+  configure_file(
+    input: 'wrap-for-bin.sh',
+    output: fs.stem(executable.full_path()),
+    configuration: executable_config,
+  )
+endforeach
diff --git a/contrib/completion/meson.build b/contrib/completion/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..a9bfe2da312ac9764367b656bd8a4099b575ad72
--- /dev/null
+++ b/contrib/completion/meson.build
@@ -0,0 +1,8 @@
+foreach script : [
+  'git-completion.bash',
+  'git-completion.tcsh',
+  'git-completion.zsh',
+  'git-prompt.sh'
+]
+  test_dependencies += fs.copyfile(script)
+endforeach
diff --git a/contrib/meson.build b/contrib/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..a7b77b87c22c8f1d8a86faaafc2b647b94651db8
--- /dev/null
+++ b/contrib/meson.build
@@ -0,0 +1 @@
+subdir('completion')
diff --git a/gitweb/meson.build b/gitweb/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..ef868bd7dbcbcb5b69c2e73fc40c374435205c9c
--- /dev/null
+++ b/gitweb/meson.build
@@ -0,0 +1,79 @@
+gitweb_config = configuration_data()
+gitweb_config.set_quoted('PERL_PATH', perl.full_path())
+gitweb_config.set_quoted('CSSMIN', '')
+gitweb_config.set_quoted('JSMIN', '')
+gitweb_config.set_quoted('GIT_BINDIR', get_option('prefix') / get_option('bindir'))
+gitweb_config.set_quoted('GITWEB_CONFIG', get_option('gitweb_config'))
+gitweb_config.set_quoted('GITWEB_CONFIG_SYSTEM', get_option('gitweb_config_system'))
+gitweb_config.set_quoted('GITWEB_CONFIG_COMMON', get_option('gitweb_config_common'))
+gitweb_config.set_quoted('GITWEB_HOME_LINK_STR', get_option('gitweb_home_link_str'))
+gitweb_config.set_quoted('GITWEB_SITENAME', get_option('gitweb_sitename'))
+gitweb_config.set_quoted('GITWEB_PROJECTROOT', get_option('gitweb_projectroot'))
+gitweb_config.set_quoted('GITWEB_PROJECT_MAXDEPTH', get_option('gitweb_project_maxdepth'))
+gitweb_config.set_quoted('GITWEB_EXPORT_OK', get_option('gitweb_export_ok'))
+gitweb_config.set_quoted('GITWEB_STRICT_EXPORT', get_option('gitweb_strict_export'))
+gitweb_config.set_quoted('GITWEB_BASE_URL', get_option('gitweb_base_url'))
+gitweb_config.set_quoted('GITWEB_LIST', get_option('gitweb_list'))
+gitweb_config.set_quoted('GITWEB_HOMETEXT', get_option('gitweb_hometext'))
+gitweb_config.set_quoted('GITWEB_CSS', get_option('gitweb_css'))
+gitweb_config.set_quoted('GITWEB_LOGO', get_option('gitweb_logo'))
+gitweb_config.set_quoted('GITWEB_FAVICON', get_option('gitweb_favicon'))
+gitweb_config.set_quoted('GITWEB_JS', get_option('gitweb_js'))
+gitweb_config.set_quoted('GITWEB_SITE_HTML_HEAD_STRING', get_option('gitweb_site_html_head_string'))
+gitweb_config.set_quoted('GITWEB_SITE_HEADER', get_option('gitweb_site_header'))
+gitweb_config.set_quoted('GITWEB_SITE_FOOTER', get_option('gitweb_site_footer'))
+gitweb_config.set_quoted('HIGHLIGHT_BIN', get_option('highlight_bin'))
+
+configure_file(
+  input: 'GITWEB-BUILD-OPTIONS.in',
+  output: 'GITWEB-BUILD-OPTIONS',
+  configuration: gitweb_config,
+)
+
+test_dependencies += custom_target(
+  input: 'gitweb.perl',
+  output: 'gitweb.cgi',
+  command: [
+    shell,
+    meson.current_source_dir() / 'generate-gitweb-cgi.sh',
+    meson.current_build_dir() / 'GITWEB-BUILD-OPTIONS',
+    git_version_file.full_path(),
+    '@INPUT@',
+    '@OUTPUT@',
+  ],
+  install: true,
+  install_dir: get_option('datadir') / 'gitweb',
+  depends: [git_version_file],
+)
+
+javascript_files = [
+  meson.current_source_dir() / 'static/js/adjust-timezone.js',
+  meson.current_source_dir() / 'static/js/blame_incremental.js',
+  meson.current_source_dir() / 'static/js/javascript-detection.js',
+  meson.current_source_dir() / 'static/js/lib/common-lib.js',
+  meson.current_source_dir() / 'static/js/lib/cookies.js',
+  meson.current_source_dir() / 'static/js/lib/datetime.js',
+]
+
+test_dependencies += custom_target(
+  input: javascript_files,
+  output: 'gitweb.js',
+  command: [
+    shell,
+    meson.current_source_dir() / 'generate-gitweb-js.sh',
+    '@OUTPUT@',
+  ] + javascript_files,
+  install: true,
+  install_dir: get_option('datadir') / 'gitweb/static',
+)
+
+foreach asset : [
+  'static/git-favicon.png',
+  'static/git-logo.png',
+  'static/gitweb.css',
+]
+  fs.copyfile(asset,
+    install: true,
+    install_dir: get_option('datadir') / 'gitweb' / fs.parent(asset),
+  )
+endforeach
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..17974eafdd86d9c1b6696868f1497b71caa165a3
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,1852 @@
+# Meson build system
+# ==================
+#
+# The Meson build system is an alternative to our Makefile that you can use to
+# build, test and install Git. Using Meson results in a couple of benefits:
+#
+#  - Out-of-tree builds.
+#  - Better integration into IDEs.
+#  - Easy-to-use autoconfiguration of available features on your system.
+#
+# To use Meson from the command line you need to have both Meson and Ninja
+# installed. Alternatively, if you do not have Python available on your system,
+# you can also use Muon instead of Meson and Samurai instead of Ninja, both of
+# which are drop-ins replacement that only depend on C.
+#
+# Basic usage
+# ===========
+#
+# In the most trivial case, you can configure, build and install Git like this:
+#
+#  1. Set up the build directory. This only needs to happen once per build
+#     directory you want to have. You can also configure multiple different
+#     build directories with different configurations.
+#
+#      $ meson setup build/
+#
+#     The build directory gets ignored by Git automatically as Meson will write
+#     a ".gitignore" file into it. From hereon, we will assume that you execute
+#     commands inside this build directory.
+#
+# 2. Compile Git. You can either use Meson, Ninja or Samurai to do this, so all
+#    of the following invocations are equivalent:
+#
+#      $ meson compile
+#      $ ninja
+#      $ samu
+#
+#   The different invocations should ultimately not make much of a difference.
+#   Using Meson also works with other generators though, like when the build
+#   directory has been set up for use with Microsoft Visual Studio.
+#
+# 3. Execute tests. Again, you can either use Meson, Ninja or Samurai to do this:
+#
+#      $ meson test
+#      $ ninja test
+#      $ samu test
+#
+#   It is recommended to use Meson in this case though as it also provides you
+#   additional features that the other build systems don't have available.
+#   You can e.g. pass additional arguments to the test executables or run
+#   individual tests:
+#
+#      # Execute the t0000-basic integration test and t-reftable-stack unit test.
+#      $ meson test t0000-basic t-reftable-stack
+#
+#      # Execute all reftable unit tests.
+#      $ meson test t-reftable-*
+#
+#      # Execute all tests and stop with the first failure.
+#      $ meson test --maxfail 1
+#
+#      # Execute single test interactively such that features like `debug ()` work.
+#      $ meson test -i --test-args='-ix' t1400-update-ref
+#
+# 4. Install the Git distribution. Again, this can be done via Meson, Ninja or
+#    Samurai:
+#
+#      $ meson install
+#      $ ninja install
+#      $ samu install
+#
+#    The prefix into which Git shall be installed is defined when setting up
+#    the build directory. More on that in the "Configuration" section.
+#
+# Meson supports multiple backends. The default backend generates Ninja build
+# instructions, but it also supports the generation of Microsoft Visual
+# Studio solutions as well as Xcode projects. IDEs like Eclipse and Visual
+# Studio Code provide plugins to import Meson files directly.
+#
+# Configuration
+# =============
+#
+# The exact configuration of Git is determined when setting up the build
+# directory via `meson setup`. Unless told otherwise, Meson will automatically
+# detect the availability of various bits and pieces. There are two different
+# kinds of options that can be used to further tweak the build:
+#
+#   - Built-in options provided by Meson.
+#
+#   - Options defined by the project in the "meson_options.txt" file.
+#
+# Both kinds of options can be inspected by running `meson configure` in the
+# build directory, which will give you a list of the current value for all
+# options.
+#
+# Options can be configured either when setting up the build directory or can
+# be changed in preexisting build directories:
+#
+#      # Set up a new build directory with optimized settings that will be
+#      # installed into an alternative prefix.
+#      $ meson setup --buildtype release --optimization 3 --strip --prefix=/home/$USER build
+#
+#      # Set up a new build directory with a higher warning level. Level 2 is
+#      # mostly equivalent to setting DEVELOPER=1, level 3 and "everything"
+#      # will enable even more warnings.
+#      $ meson setup -Dwarning_level=2
+#
+#      # Set up a new build directory with 'address' and 'undefined' sanitizers
+#      # using Clang.
+#      $ CC=clang meson setup -Db_sanitize=address,undefined build
+#
+#      # Disable tests in a preexisting build directory.
+#      $ meson configure -Dtests=false
+#
+#      # Disable features based on Python
+#      $ meson configure -Dpython=disabled
+#
+# Options have a type like booleans, choices, strings or features. Features are
+# somewhat special as they can have one of three values: enabled, disabled or
+# auto. While the first two values are self-explanatory, "auto" will enable or
+# disable the feature based on the availability of prerequisites to support it.
+# Python-based features for example will be enabled automatically when a Python
+# interpreter could be found. The default value of such features can be changed
+# globally via `meson setup --auto-features={enabled,disabled,auto}`, which
+# will set the value of all features with a value of "auto" to the provided one
+# by default.
+#
+# It is also possible to store a set of configuration options in machine files.
+# This can be useful in case you regularly want to reuse the same set of options:
+#
+#   [binaries]
+#   c = ['clang']
+#   ar = ['ar']
+#
+#   [project options]
+#   gettext = 'disabled'
+#   default_editor = 'vim'
+#
+#   [built-in options]
+#   b_lto = true
+#   b_sanitize = 'address,undefined'
+#
+# These machine files can be passed to Meson via `meson setup --native-file`.
+#
+# Subproject wrappers
+# ===================
+#
+# Subproject wrappers are a feature provided by Meson that allow the automatic
+# fallback to a "wrapped" dependency in case the dependency is not provided by
+# the system. For example if the system is lacking curl, then Meson will use
+# "subprojects/curl.wrap" to set up curl as a subproject and compile and link
+# the dependency into Git itself. This is especially helpful on systems like
+# Windows, where you typically don't have such dependencies installed.
+#
+# The use of subproject wrappers can be disabled by executing `meson setup
+# --wrap-mode nofallback`.
+
+project('git', 'c',
+  meson_version: '>=1.3.0',
+  # MSVC does not support GNU99, and C99 does not define __STDC_VERSION__
+  # on MSVC. So we instead fall back to C11 there.
+  default_options: ['c_std=gnu99,c11'],
+  version: 'v2.47.GIT',
+)
+
+fs = import('fs')
+
+program_path = []
+# Git for Windows provides all the tools we need to build Git.
+if host_machine.system() == 'windows'
+  program_path += [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ]
+endif
+
+awk = find_program('awk', dirs: program_path)
+cygpath = find_program('cygpath', dirs: program_path, required: false)
+diff = find_program('diff', dirs: program_path)
+shell = find_program('sh', dirs: program_path)
+tar = find_program('tar', dirs: program_path)
+
+script_environment = environment()
+foreach tool : ['cat', 'cut', 'git', 'grep', 'sed', 'sort', 'tr', 'uname']
+  program = find_program(tool, dirs: program_path)
+  script_environment.prepend('PATH', fs.parent(program.full_path()))
+endforeach
+
+compiler = meson.get_compiler('c')
+
+libgit_sources = [
+  'abspath.c',
+  'add-interactive.c',
+  'add-patch.c',
+  'advice.c',
+  'alias.c',
+  'alloc.c',
+  'apply.c',
+  'archive-tar.c',
+  'archive-zip.c',
+  'archive.c',
+  'attr.c',
+  'base85.c',
+  'bisect.c',
+  'blame.c',
+  'blob.c',
+  'bloom.c',
+  'branch.c',
+  'bulk-checkin.c',
+  'bundle-uri.c',
+  'bundle.c',
+  'cache-tree.c',
+  'cbtree.c',
+  'chdir-notify.c',
+  'checkout.c',
+  'chunk-format.c',
+  'color.c',
+  'column.c',
+  'combine-diff.c',
+  'commit-graph.c',
+  'commit-reach.c',
+  'commit.c',
+  'compat/nonblock.c',
+  'compat/obstack.c',
+  'compat/terminal.c',
+  'compat/zlib-uncompress2.c',
+  'config.c',
+  'connect.c',
+  'connected.c',
+  'convert.c',
+  'copy.c',
+  'credential.c',
+  'csum-file.c',
+  'ctype.c',
+  'date.c',
+  'decorate.c',
+  'delta-islands.c',
+  'diagnose.c',
+  'diff-delta.c',
+  'diff-merges.c',
+  'diff-lib.c',
+  'diff-no-index.c',
+  'diff.c',
+  'diffcore-break.c',
+  'diffcore-delta.c',
+  'diffcore-order.c',
+  'diffcore-pickaxe.c',
+  'diffcore-rename.c',
+  'diffcore-rotate.c',
+  'dir-iterator.c',
+  'dir.c',
+  'editor.c',
+  'entry.c',
+  'environment.c',
+  'ewah/bitmap.c',
+  'ewah/ewah_bitmap.c',
+  'ewah/ewah_io.c',
+  'ewah/ewah_rlw.c',
+  'exec-cmd.c',
+  'fetch-negotiator.c',
+  'fetch-pack.c',
+  'fmt-merge-msg.c',
+  'fsck.c',
+  'fsmonitor.c',
+  'fsmonitor-ipc.c',
+  'fsmonitor-settings.c',
+  'gettext.c',
+  'git-zlib.c',
+  'gpg-interface.c',
+  'graph.c',
+  'grep.c',
+  'hash-lookup.c',
+  'hashmap.c',
+  'help.c',
+  'hex.c',
+  'hex-ll.c',
+  'hook.c',
+  'ident.c',
+  'json-writer.c',
+  'kwset.c',
+  'levenshtein.c',
+  'line-log.c',
+  'line-range.c',
+  'linear-assignment.c',
+  'list-objects-filter-options.c',
+  'list-objects-filter.c',
+  'list-objects.c',
+  'lockfile.c',
+  'log-tree.c',
+  'loose.c',
+  'ls-refs.c',
+  'mailinfo.c',
+  'mailmap.c',
+  'match-trees.c',
+  'mem-pool.c',
+  'merge-blobs.c',
+  'merge-ll.c',
+  'merge-ort.c',
+  'merge-ort-wrappers.c',
+  'merge-recursive.c',
+  'merge.c',
+  'midx.c',
+  'midx-write.c',
+  'name-hash.c',
+  'negotiator/default.c',
+  'negotiator/noop.c',
+  'negotiator/skipping.c',
+  'notes-cache.c',
+  'notes-merge.c',
+  'notes-utils.c',
+  'notes.c',
+  'object-file-convert.c',
+  'object-file.c',
+  'object-name.c',
+  'object.c',
+  'oid-array.c',
+  'oidmap.c',
+  'oidset.c',
+  'oidtree.c',
+  'pack-bitmap-write.c',
+  'pack-bitmap.c',
+  'pack-check.c',
+  'pack-mtimes.c',
+  'pack-objects.c',
+  'pack-revindex.c',
+  'pack-write.c',
+  'packfile.c',
+  'pager.c',
+  'parallel-checkout.c',
+  'parse.c',
+  'parse-options-cb.c',
+  'parse-options.c',
+  'patch-delta.c',
+  'patch-ids.c',
+  'path.c',
+  'pathspec.c',
+  'pkt-line.c',
+  'preload-index.c',
+  'pretty.c',
+  'prio-queue.c',
+  'progress.c',
+  'promisor-remote.c',
+  'prompt.c',
+  'protocol.c',
+  'protocol-caps.c',
+  'prune-packed.c',
+  'pseudo-merge.c',
+  'quote.c',
+  'range-diff.c',
+  'reachable.c',
+  'read-cache.c',
+  'rebase-interactive.c',
+  'rebase.c',
+  'ref-filter.c',
+  'reflog-walk.c',
+  'reflog.c',
+  'refs.c',
+  'refs/debug.c',
+  'refs/files-backend.c',
+  'refs/reftable-backend.c',
+  'refs/iterator.c',
+  'refs/packed-backend.c',
+  'refs/ref-cache.c',
+  'refspec.c',
+  'reftable/basics.c',
+  'reftable/error.c',
+  'reftable/block.c',
+  'reftable/blocksource.c',
+  'reftable/iter.c',
+  'reftable/merged.c',
+  'reftable/pq.c',
+  'reftable/reader.c',
+  'reftable/record.c',
+  'reftable/stack.c',
+  'reftable/tree.c',
+  'reftable/writer.c',
+  'remote.c',
+  'replace-object.c',
+  'repo-settings.c',
+  'repository.c',
+  'rerere.c',
+  'reset.c',
+  'resolve-undo.c',
+  'revision.c',
+  'run-command.c',
+  'send-pack.c',
+  'sequencer.c',
+  'serve.c',
+  'server-info.c',
+  'setup.c',
+  'shallow.c',
+  'sideband.c',
+  'sigchain.c',
+  'sparse-index.c',
+  'split-index.c',
+  'stable-qsort.c',
+  'statinfo.c',
+  'strbuf.c',
+  'streaming.c',
+  'string-list.c',
+  'strmap.c',
+  'strvec.c',
+  'sub-process.c',
+  'submodule-config.c',
+  'submodule.c',
+  'symlinks.c',
+  'tag.c',
+  'tempfile.c',
+  'thread-utils.c',
+  'tmp-objdir.c',
+  'trace.c',
+  'trace2.c',
+  'trace2/tr2_cfg.c',
+  'trace2/tr2_cmd_name.c',
+  'trace2/tr2_ctr.c',
+  'trace2/tr2_dst.c',
+  'trace2/tr2_sid.c',
+  'trace2/tr2_sysenv.c',
+  'trace2/tr2_tbuf.c',
+  'trace2/tr2_tgt_event.c',
+  'trace2/tr2_tgt_normal.c',
+  'trace2/tr2_tgt_perf.c',
+  'trace2/tr2_tls.c',
+  'trace2/tr2_tmr.c',
+  'trailer.c',
+  'transport-helper.c',
+  'transport.c',
+  'tree-diff.c',
+  'tree-walk.c',
+  'tree.c',
+  'unpack-trees.c',
+  'upload-pack.c',
+  'url.c',
+  'urlmatch.c',
+  'usage.c',
+  'userdiff.c',
+  'utf8.c',
+  'varint.c',
+  'versioncmp.c',
+  'walker.c',
+  'wildmatch.c',
+  'worktree.c',
+  'wrapper.c',
+  'write-or-die.c',
+  'ws.c',
+  'wt-status.c',
+  'xdiff-interface.c',
+  'xdiff/xdiffi.c',
+  'xdiff/xemit.c',
+  'xdiff/xhistogram.c',
+  'xdiff/xmerge.c',
+  'xdiff/xpatience.c',
+  'xdiff/xprepare.c',
+  'xdiff/xutils.c',
+]
+
+builtin_sources = [
+  'builtin/add.c',
+  'builtin/am.c',
+  'builtin/annotate.c',
+  'builtin/apply.c',
+  'builtin/archive.c',
+  'builtin/bisect.c',
+  'builtin/blame.c',
+  'builtin/branch.c',
+  'builtin/bugreport.c',
+  'builtin/bundle.c',
+  'builtin/cat-file.c',
+  'builtin/check-attr.c',
+  'builtin/check-ignore.c',
+  'builtin/check-mailmap.c',
+  'builtin/check-ref-format.c',
+  'builtin/checkout--worker.c',
+  'builtin/checkout-index.c',
+  'builtin/checkout.c',
+  'builtin/clean.c',
+  'builtin/clone.c',
+  'builtin/column.c',
+  'builtin/commit-graph.c',
+  'builtin/commit-tree.c',
+  'builtin/commit.c',
+  'builtin/config.c',
+  'builtin/count-objects.c',
+  'builtin/credential-cache--daemon.c',
+  'builtin/credential-cache.c',
+  'builtin/credential-store.c',
+  'builtin/credential.c',
+  'builtin/describe.c',
+  'builtin/diagnose.c',
+  'builtin/diff-files.c',
+  'builtin/diff-index.c',
+  'builtin/diff-tree.c',
+  'builtin/diff.c',
+  'builtin/difftool.c',
+  'builtin/fast-export.c',
+  'builtin/fast-import.c',
+  'builtin/fetch-pack.c',
+  'builtin/fetch.c',
+  'builtin/fmt-merge-msg.c',
+  'builtin/for-each-ref.c',
+  'builtin/for-each-repo.c',
+  'builtin/fsck.c',
+  'builtin/fsmonitor--daemon.c',
+  'builtin/gc.c',
+  'builtin/get-tar-commit-id.c',
+  'builtin/grep.c',
+  'builtin/hash-object.c',
+  'builtin/help.c',
+  'builtin/hook.c',
+  'builtin/index-pack.c',
+  'builtin/init-db.c',
+  'builtin/interpret-trailers.c',
+  'builtin/log.c',
+  'builtin/ls-files.c',
+  'builtin/ls-remote.c',
+  'builtin/ls-tree.c',
+  'builtin/mailinfo.c',
+  'builtin/mailsplit.c',
+  'builtin/merge-base.c',
+  'builtin/merge-file.c',
+  'builtin/merge-index.c',
+  'builtin/merge-ours.c',
+  'builtin/merge-recursive.c',
+  'builtin/merge-tree.c',
+  'builtin/merge.c',
+  'builtin/mktag.c',
+  'builtin/mktree.c',
+  'builtin/multi-pack-index.c',
+  'builtin/mv.c',
+  'builtin/name-rev.c',
+  'builtin/notes.c',
+  'builtin/pack-objects.c',
+  'builtin/pack-redundant.c',
+  'builtin/pack-refs.c',
+  'builtin/patch-id.c',
+  'builtin/prune-packed.c',
+  'builtin/prune.c',
+  'builtin/pull.c',
+  'builtin/push.c',
+  'builtin/range-diff.c',
+  'builtin/read-tree.c',
+  'builtin/rebase.c',
+  'builtin/receive-pack.c',
+  'builtin/reflog.c',
+  'builtin/refs.c',
+  'builtin/remote-ext.c',
+  'builtin/remote-fd.c',
+  'builtin/remote.c',
+  'builtin/repack.c',
+  'builtin/replace.c',
+  'builtin/replay.c',
+  'builtin/rerere.c',
+  'builtin/reset.c',
+  'builtin/rev-list.c',
+  'builtin/rev-parse.c',
+  'builtin/revert.c',
+  'builtin/rm.c',
+  'builtin/send-pack.c',
+  'builtin/shortlog.c',
+  'builtin/show-branch.c',
+  'builtin/show-index.c',
+  'builtin/show-ref.c',
+  'builtin/sparse-checkout.c',
+  'builtin/stash.c',
+  'builtin/stripspace.c',
+  'builtin/submodule--helper.c',
+  'builtin/symbolic-ref.c',
+  'builtin/tag.c',
+  'builtin/unpack-file.c',
+  'builtin/unpack-objects.c',
+  'builtin/update-index.c',
+  'builtin/update-ref.c',
+  'builtin/update-server-info.c',
+  'builtin/upload-archive.c',
+  'builtin/upload-pack.c',
+  'builtin/var.c',
+  'builtin/verify-commit.c',
+  'builtin/verify-pack.c',
+  'builtin/verify-tag.c',
+  'builtin/worktree.c',
+  'builtin/write-tree.c',
+]
+
+libgit_sources += custom_target(
+  input: 'command-list.txt',
+  output: 'command-list.h',
+  command: [shell, meson.current_source_dir() + '/generate-cmdlist.sh', meson.current_source_dir(), '@OUTPUT@'],
+  env: script_environment,
+)
+
+libgit_sources += custom_target(
+  output: 'config-list.h',
+  command: [
+    shell,
+    meson.current_source_dir() + '/generate-configlist.sh',
+    meson.current_source_dir(),
+    '@OUTPUT@',
+  ],
+  env: script_environment,
+)
+
+libgit_sources += custom_target(
+  input: 'Documentation/githooks.txt',
+  output: 'hook-list.h',
+  command: [
+    shell,
+    meson.current_source_dir() + '/generate-hooklist.sh',
+    meson.current_source_dir(),
+    '@OUTPUT@',
+  ],
+  env: script_environment,
+)
+
+# This contains the variables for GIT-BUILD-OPTIONS, which we use to propagate
+# build options to our tests.
+build_options_config = configuration_data()
+build_options_config.set('GIT_INTEROP_MAKE_OPTS', '')
+build_options_config.set('GIT_PERF_LARGE_REPO', '')
+build_options_config.set('GIT_PERF_MAKE_COMMAND', '')
+build_options_config.set('GIT_PERF_MAKE_OPTS', '')
+build_options_config.set('GIT_PERF_REPEAT_COUNT', '')
+build_options_config.set('GIT_PERF_REPO', '')
+build_options_config.set('GIT_TEST_CMP_USE_COPIED_CONTEXT', '')
+build_options_config.set('GIT_TEST_INDEX_VERSION', '')
+build_options_config.set('GIT_TEST_OPTS', '')
+build_options_config.set('GIT_TEST_PERL_FATAL_WARNINGS', '')
+build_options_config.set('GIT_TEST_UTF8_LOCALE', '')
+build_options_config.set('BROKEN_PATH_FIX', '')
+build_options_config.set_quoted('LOCALEDIR', fs.as_posix(get_option('prefix') / get_option('localedir')))
+build_options_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))
+
+test_output_directory = get_option('test_output_directory')
+if test_output_directory == ''
+  test_output_directory = meson.project_build_root() / 'test-output'
+endif
+
+# These variables are used for building libgit.a.
+libgit_c_args = [
+  '-DBINDIR="' + get_option('bindir') + '"',
+  '-DDEFAULT_EDITOR="' + get_option('default_editor') + '"',
+  '-DDEFAULT_GIT_TEMPLATE_DIR="' + get_option('datadir') / 'git-core/templates' + '"',
+  '-DDEFAULT_HELP_FORMAT="' + get_option('default_help_format') + '"',
+  '-DDEFAULT_PAGER="' + get_option('default_pager') + '"',
+  '-DETC_GITATTRIBUTES="' + get_option('gitattributes') + '"',
+  '-DETC_GITCONFIG="' + get_option('gitconfig') + '"',
+  '-DFALLBACK_RUNTIME_PREFIX="' + get_option('prefix') + '"',
+  '-DGIT_EXEC_PATH="' + get_option('prefix') / get_option('libexecdir') / 'git-core"',
+  '-DGIT_HOST_CPU="' + host_machine.cpu_family() + '"',
+  '-DGIT_HTML_PATH="' + get_option('datadir') / 'doc/git-doc"',
+  '-DGIT_INFO_PATH="' + get_option('infodir') + '"',
+  '-DGIT_LOCALE_PATH="' + get_option('localedir') + '"',
+  '-DGIT_MAN_PATH="' + get_option('mandir') + '"',
+  '-DPAGER_ENV="' + get_option('pager_environment') + '"',
+  '-DSHELL_PATH="' + fs.as_posix(shell.full_path()) + '"',
+]
+libgit_include_directories = [ '.' ]
+libgit_dependencies = [ ]
+
+# Treat any warning level above 1 the same as we treat DEVELOPER=1 in our
+# Makefile.
+if get_option('warning_level') in ['2','3', 'everything'] and compiler.get_argument_syntax() == 'gcc'
+  foreach cflag : [
+    '-Wdeclaration-after-statement',
+    '-Wformat-security',
+    '-Wold-style-definition',
+    '-Woverflow',
+    '-Wpointer-arith',
+    '-Wstrict-prototypes',
+    '-Wunused',
+    '-Wvla',
+    '-Wwrite-strings',
+    '-fno-common',
+    '-Wtautological-constant-out-of-range-compare',
+    # If a function is public, there should be a prototype and the right
+    # header file should be included. If not, it should be static.
+    '-Wmissing-prototypes',
+    # These are disabled because we have these all over the place.
+    '-Wno-empty-body',
+    '-Wno-missing-field-initializers',
+    '-Wno-sign-compare',
+  ]
+    if compiler.has_argument(cflag)
+      libgit_c_args += cflag
+    endif
+  endforeach
+endif
+
+if get_option('b_sanitize').contains('address')
+  build_options_config.set('SANITIZE_ADDRESS', 'YesCompiledWithIt')
+else
+  build_options_config.set('SANITIZE_ADDRESS', '')
+endif
+if get_option('b_sanitize').contains('leak')
+  libgit_c_args += '-DSUPPRESS_ANNOTATED_LEAKS'
+  build_options_config.set('SANITIZE_LEAK', 'YesCompiledWithIt')
+else
+  build_options_config.set('SANITIZE_LEAK', '')
+endif
+if get_option('b_sanitize').contains('undefined')
+  libgit_c_args += '-DSHA1DC_FORCE_ALIGNED_ACCESS'
+endif
+
+executable_suffix = ''
+if host_machine.system() == 'cygwin' or host_machine.system() == 'windows'
+  executable_suffix = '.exe'
+  libgit_c_args += '-DSTRIP_EXTENSION="' + executable_suffix + '"'
+endif
+build_options_config.set_quoted('X', executable_suffix)
+
+python = import('python').find_installation('python3', required: get_option('python'))
+if python.found()
+  build_options_config.set('NO_PYTHON', '')
+else
+  libgit_c_args += '-DNO_PYTHON'
+  build_options_config.set('NO_PYTHON', '1')
+endif
+
+# Perl is used for two different things: our test harness and to provide some
+# features. It is optional if you want to neither execute tests nor use any of
+# these optional features.
+perl_required = get_option('perl')
+if get_option('tests')
+  perl_required = true
+endif
+
+# Note that we only set NO_PERL if the Perl features were disabled by the user.
+# It may not be set when we have found Perl, but only use it to run tests.
+perl = find_program('perl', version: '>=5.8.1', dirs: program_path, required: perl_required)
+perl_features_enabled = perl.found() and get_option('perl').allowed()
+if perl_features_enabled
+  build_options_config.set('NO_PERL', '')
+
+  if get_option('runtime_prefix')
+    build_options_config.set('PERL_LOCALEDIR', '')
+  else
+    build_options_config.set_quoted('PERL_LOCALEDIR', fs.as_posix(get_option('prefix') / get_option('localedir')))
+  endif
+
+  if get_option('perl_cpan_fallback')
+    build_options_config.set('NO_PERL_CPAN_FALLBACKS', '')
+  else
+    build_options_config.set_quoted('NO_PERL_CPAN_FALLBACKS', 'YesPlease')
+  endif
+else
+  libgit_c_args += '-DNO_PERL'
+  build_options_config.set('NO_PERL', '1')
+  build_options_config.set('PERL_LOCALEDIR', '')
+  build_options_config.set('NO_PERL_CPAN_FALLBACKS', '')
+endif
+
+zlib = dependency('zlib', default_options: ['default_library=static', 'tests=disabled'])
+if zlib.version().version_compare('<1.2.0')
+  libgit_c_args += '-DNO_DEFLATE_BOUND'
+endif
+libgit_dependencies += zlib
+
+threads = dependency('threads', required: false)
+if threads.found()
+  libgit_dependencies += threads
+  build_options_config.set('NO_PTHREADS', '')
+else
+  libgit_c_args += '-DNO_PTHREADS'
+  build_options_config.set('NO_PTHREADS', '1')
+endif
+
+msgfmt = find_program('msgfmt', dirs: program_path, required: false)
+gettext_option = get_option('gettext').disable_auto_if(not msgfmt.found())
+if not msgfmt.found() and gettext_option.enabled()
+  error('Internationalization via libintl requires msgfmt')
+endif
+
+if gettext_option.allowed() and host_machine.system() == 'darwin' and get_option('macos_use_homebrew_gettext')
+  if host_machine.cpu_family() == 'x86_64'
+    libintl_prefix = '/usr/local'
+  elif host_machine.cpu_family() == 'aarch64'
+    libintl_prefix = '/opt/homebrew'
+  else
+    error('Homebrew workaround not supported on current architecture')
+  endif
+
+  intl = compiler.find_library('intl', dirs: libintl_prefix / 'lib', required: gettext_option)
+  if intl.found()
+    intl = declare_dependency(
+      dependencies: intl,
+      include_directories: libintl_prefix / 'include',
+    )
+  endif
+else
+  intl = dependency('intl', required: gettext_option)
+endif
+if intl.found()
+  libgit_dependencies += intl
+  build_options_config.set('NO_GETTEXT', '')
+  build_options_config.set('USE_GETTEXT_SCHEME', '')
+else
+  libgit_c_args += '-DNO_GETTEXT'
+  build_options_config.set('NO_GETTEXT', '1')
+  build_options_config.set('USE_GETTEXT_SCHEME', 'fallthrough')
+endif
+
+iconv = dependency('iconv', required: get_option('iconv'))
+if iconv.found()
+  libgit_dependencies += iconv
+  build_options_config.set('NO_ICONV', '')
+
+  have_old_iconv = false
+  if not compiler.compiles('''
+    #include <iconv.h>
+
+    extern size_t iconv(iconv_t cd,
+                        char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft);
+  ''', name: 'old iconv interface', dependencies: [iconv])
+    libgit_c_args += '-DOLD_ICONV'
+    have_old_iconv = true
+  endif
+
+  iconv_omits_bom_source = '''#
+    #include <iconv.h>
+
+    int main(int argc, const char **argv)
+    {
+  '''
+  if have_old_iconv
+    iconv_omits_bom_source += '''
+      typedef const char *iconv_ibp;
+    '''
+  else
+    iconv_omits_bom_source += '''
+      typedef char *iconv_ibp;
+    '''
+  endif
+  iconv_omits_bom_source += '''
+      int v;
+      iconv_t conv;
+      char in[] = "a"; iconv_ibp pin = in;
+      char out[20] = ""; char *pout = out;
+      size_t isz = sizeof in;
+      size_t osz = sizeof out;
+
+      conv = iconv_open("UTF-16", "UTF-8");
+      iconv(conv, &pin, &isz, &pout, &osz);
+      iconv_close(conv);
+      v = (unsigned char)(out[0]) + (unsigned char)(out[1]);
+      return v != 0xfe + 0xff;
+    }
+  '''
+
+  if compiler.run(iconv_omits_bom_source,
+    dependencies: iconv,
+    name: 'iconv omits BOM',
+  ).returncode() != 0
+    libgit_c_args += '-DICONV_OMITS_BOM'
+  endif
+else
+  libgit_c_args += '-DNO_ICONV'
+  build_options_config.set('NO_ICONV', '1')
+endif
+
+pcre2 = dependency('libpcre2-8', required: get_option('pcre2'), default_options: ['default_library=static', 'test=false'])
+if pcre2.found()
+  libgit_dependencies += pcre2
+  libgit_c_args += '-DUSE_LIBPCRE2'
+  build_options_config.set('USE_LIBPCRE2', '1')
+else
+  build_options_config.set('USE_LIBPCRE2', '')
+endif
+
+curl = dependency('libcurl', version: '>=7.21.3', required: get_option('curl'), default_options: ['default_library=static', 'tests=disabled', 'tool=disabled'])
+use_curl_for_imap_send = false
+if curl.found()
+  if curl.version().version_compare('>=7.34.0')
+    libgit_c_args += '-DUSE_CURL_FOR_IMAP_SEND'
+    use_curl_for_imap_send = true
+  endif
+
+  libgit_dependencies += curl
+  libgit_c_args += '-DCURL_DISABLE_TYPECHECK'
+  build_options_config.set('NO_CURL', '')
+else
+  libgit_c_args += '-DNO_CURL'
+  build_options_config.set('NO_CURL', '1')
+endif
+
+expat = dependency('expat', required: get_option('expat'), default_options: ['default_library=static', 'build_tests=false'])
+if expat.found()
+  libgit_dependencies += expat
+
+  if expat.version().version_compare('<=1.2')
+    libgit_c_args += '-DEXPAT_NEEDS_XMLPARSE_H'
+  endif
+  build_options_config.set('NO_EXPAT', '')
+else
+  libgit_c_args += '-DNO_EXPAT'
+  build_options_config.set('NO_EXPAT', '1')
+endif
+
+if not compiler.has_header('sys/select.h')
+  libgit_c_args += '-DNO_SYS_SELECT_H'
+endif
+
+has_poll_h = compiler.has_header('poll.h')
+if not has_poll_h
+  libgit_c_args += '-DNO_POLL_H'
+endif
+
+has_sys_poll_h = compiler.has_header('sys/poll.h')
+if not has_sys_poll_h
+  libgit_c_args += '-DNO_SYS_POLL_H'
+endif
+
+if not has_poll_h and not has_sys_poll_h
+  libgit_c_args += '-DNO_POLL'
+  libgit_sources += 'compat/poll/poll.c'
+  libgit_include_directories += 'compat/poll'
+endif
+
+if not compiler.has_header('inttypes.h')
+  libgit_c_args += '-DNO_INTTYPES_H'
+endif
+
+if compiler.has_header('libcharset.h')
+  libcharset = compiler.find_library('charset')
+
+  if compiler.has_function('locale_charset',
+    prefix: '#include <libcharset.h>',
+    dependencies: iconv,
+  )
+    libgit_c_args += '-DHAVE_LIBCHARSET_H'
+  elif compiler.has_function('locale_charset',
+    prefix: '#include <libcharset.h>',
+    dependencies: libcharset,
+  )
+    libgit_c_args += '-DHAVE_LIBCHARSET_H'
+  endif
+endif
+
+if compiler.has_header('alloca.h')
+  libgit_c_args += '-DHAVE_ALLOCA_H'
+endif
+
+if compiler.has_header('sys/sysinfo.h')
+  libgit_c_args += '-DHAVE_SYSINFO'
+endif
+
+# Windows has libgen.h and a basename implementation, but we still need our own
+# implementation to threat things like drive prefixes specially.
+if host_machine.system() == 'windows' or not compiler.has_header('libgen.h')
+  libgit_c_args += '-DNO_LIBGEN_H'
+  libgit_sources += 'compat/basename.c'
+endif
+
+if compiler.has_header('paths.h')
+  libgit_c_args += '-DHAVE_PATHS_H'
+endif
+
+if compiler.has_header('strings.h')
+  libgit_c_args += '-DHAVE_STRINGS_H'
+endif
+
+networking_dependencies = [ ]
+if host_machine.system() == 'windows'
+  winsock = compiler.find_library('ws2_32', required: false)
+  if winsock.found()
+    networking_dependencies += winsock
+  endif
+else
+  libresolv = compiler.find_library('resolv', required: false)
+  if libresolv.found()
+    networking_dependencies += libresolv
+  endif
+endif
+libgit_dependencies += networking_dependencies
+
+foreach symbol : ['inet_ntop', 'inet_pton', 'strerror']
+  if not compiler.has_function(symbol, dependencies: networking_dependencies)
+    libgit_c_args += '-DNO_' + symbol.to_upper()
+  endif
+endforeach
+
+has_ipv6 = compiler.has_function('getaddrinfo', dependencies: networking_dependencies)
+if not has_ipv6
+  libgit_c_args += '-DNO_IPV6'
+endif
+
+if not compiler.compiles('''
+  #ifdef _WIN32
+  # include <winsock2.h>
+  #else
+  # include <sys/types.h>
+  # include <sys/socket.h>
+  #endif
+
+  void func(void)
+  {
+    struct sockaddr_storage x;
+  }
+''', name: 'struct sockaddr_storage')
+  if has_ipv6
+    libgit_c_args += '-Dsockaddr_storage=sockaddr_in6'
+  else
+    libgit_c_args += '-Dsockaddr_storage=sockaddr_in'
+  endif
+endif
+
+if compiler.has_function('socket', dependencies: networking_dependencies)
+  libgit_sources += [
+    'unix-socket.c',
+    'unix-stream-server.c',
+  ]
+  build_options_config.set('NO_UNIX_SOCKETS', '')
+else
+  libgit_c_args += '-DNO_UNIX_SOCKETS'
+  build_options_config.set('NO_UNIX_SOCKETS', '1')
+endif
+
+if not compiler.has_function('pread')
+  libgit_c_args += '-DNO_PREAD'
+  libgit_sources += 'compat/pread.c'
+endif
+
+if host_machine.system() == 'darwin'
+  libgit_sources += 'compat/precompose_utf8.c'
+  libgit_c_args += '-DPRECOMPOSE_UNICODE'
+  libgit_c_args += '-DPROTECT_HFS_DEFAULT'
+endif
+
+# Configure general compatibility wrappers.
+if host_machine.system() == 'cygwin'
+  libgit_sources += [
+    'compat/win32/path-utils.c',
+  ]
+elif host_machine.system() == 'windows'
+  libgit_sources += [
+    'compat/mingw.c',
+    'compat/winansi.c',
+    'compat/win32/flush.c',
+    'compat/win32/path-utils.c',
+    'compat/win32/pthread.c',
+    'compat/win32/syslog.c',
+    'compat/win32/dirent.c',
+    'compat/win32mmap.c',
+    'compat/nedmalloc/nedmalloc.c',
+  ]
+
+  libgit_c_args += [
+    '-DDETECT_MSYS_TTY',
+    '-DENSURE_MSYSTEM_IS_SET',
+    '-DNATIVE_CRLF',
+    '-DNOGDI',
+    '-DNO_POSIX_GOODIES',
+    '-DWIN32',
+    '-D_CONSOLE',
+    '-D_CONSOLE_DETECT_MSYS_TTY',
+    '-D__USE_MINGW_ANSI_STDIO=0',
+  ]
+
+  libgit_dependencies += compiler.find_library('ntdll')
+  libgit_include_directories += 'compat/win32'
+  if compiler.get_id() == 'msvc'
+    libgit_include_directories += 'compat/vcbuild/include'
+  endif
+endif
+
+if host_machine.system() == 'linux'
+  libgit_sources += 'compat/linux/procinfo.c'
+elif host_machine.system() == 'windows'
+  libgit_sources += 'compat/win32/trace2_win32_process_info.c'
+else
+  libgit_sources += 'compat/stub/procinfo.c'
+endif
+
+if host_machine.system() == 'cygwin' or host_machine.system() == 'windows'
+  libgit_c_args += [
+    '-DUNRELIABLE_FSTAT',
+    '-DMMAP_PREVENTS_DELETE',
+    '-DOBJECT_CREATION_MODE=1',
+  ]
+endif
+
+# Configure the simple-ipc subsystem required fro the fsmonitor.
+if host_machine.system() == 'windows'
+  libgit_sources += [
+    'compat/simple-ipc/ipc-shared.c',
+    'compat/simple-ipc/ipc-win32.c',
+  ]
+  libgit_c_args += '-DSUPPORTS_SIMPLE_IPC'
+else
+  libgit_sources += [
+    'compat/simple-ipc/ipc-shared.c',
+    'compat/simple-ipc/ipc-unix-socket.c',
+  ]
+  libgit_c_args += '-DSUPPORTS_SIMPLE_IPC'
+endif
+
+fsmonitor_backend = ''
+if host_machine.system() == 'windows'
+  fsmonitor_backend = 'win32'
+elif host_machine.system() == 'darwin'
+  fsmonitor_backend = 'darwin'
+  libgit_dependencies += dependency('CoreServices')
+endif
+if fsmonitor_backend != ''
+  libgit_c_args += '-DHAVE_FSMONITOR_DAEMON_BACKEND'
+  libgit_c_args += '-DHAVE_FSMONITOR_OS_SETTINGS'
+
+  libgit_sources += [
+    'compat/fsmonitor/fsm-health-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-ipc-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-listen-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-path-utils-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-settings-' + fsmonitor_backend + '.c',
+  ]
+endif
+build_options_config.set_quoted('FSMONITOR_DAEMON_BACKEND', fsmonitor_backend)
+build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_backend)
+
+if not get_option('b_sanitize').contains('address') and get_option('regex').allowed() and compiler.has_header('regex.h') and compiler.get_define('REG_STARTEND', prefix: '#include <regex.h>') != ''
+  build_options_config.set('NO_REGEX', '')
+
+  if compiler.get_define('REG_ENHANCED', prefix: '#include <regex.h>') != ''
+    libgit_c_args += '-DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS'
+    libgit_sources += 'compat/regcomp_enhanced.c'
+  endif
+elif not get_option('regex').enabled()
+  libgit_c_args += [
+    '-DNO_REGEX',
+    '-DGAWK',
+    '-DNO_MBSUPPORT',
+  ]
+  build_options_config.set('NO_REGEX', '1')
+  libgit_sources += 'compat/regex/regex.c'
+  libgit_include_directories += 'compat/regex'
+else
+    error('Native regex support requested but not found')
+endif
+
+# setitimer and friends are provided by compat/mingw.c.
+if host_machine.system() != 'windows'
+  if not compiler.compiles('''
+    #include <sys/time.h>
+    void func(void)
+    {
+      struct itimerval value;
+    }
+  ''', name: 'struct itimerval')
+    libgit_c_args += '-DNO_STRUCT_ITIMERVAL'
+    libgit_c_args += '-DNO_SETITIMER'
+  elif not compiler.has_function('setitimer')
+    libgit_c_args += '-DNO_SETITIMER'
+  endif
+endif
+
+if compiler.has_member('struct stat', 'st_mtimespec.tv_nsec', prefix: '#include <sys/stat.h>')
+  libgit_c_args += '-DUSE_ST_TIMESPEC'
+elif not compiler.has_member('struct stat', 'st_mtim.tv_nsec', prefix: '#include <sys/stat.h>')
+  libgit_c_args += '-DNO_NSEC'
+endif
+
+if not compiler.has_member('struct stat', 'st_blocks', prefix: '#include <sys/stat.h>')
+  libgit_c_args += '-DNO_ST_BLOCKS_IN_STRUCT_STAT'
+endif
+
+if not compiler.has_member('struct dirent', 'd_type', prefix: '#include <dirent.h>')
+  libgit_c_args += '-DNO_D_TYPE_IN_DIRENT'
+endif
+
+if not compiler.has_member('struct passwd', 'pw_gecos', prefix: '#include <pwd.h>')
+  libgit_c_args += '-DNO_GECOS_IN_PWENT'
+endif
+
+if compiler.has_function('sync_file_range')
+  libgit_c_args += '-DHAVE_SYNC_FILE_RANGE'
+endif
+
+if not compiler.has_function('strcasestr')
+  libgit_c_args += '-DNO_STRCASESTR'
+  libgit_sources += 'compat/strcasestr.c'
+endif
+
+if not compiler.has_function('memmem')
+  libgit_c_args += '-DNO_MEMMEM'
+  libgit_sources += 'compat/memmem.c'
+endif
+
+if not compiler.has_function('strlcpy')
+  libgit_c_args += '-DNO_STRLCPY'
+  libgit_sources += 'compat/strlcpy.c'
+endif
+
+if not compiler.has_function('strdup')
+  libgit_c_args += '-DOVERRIDE_STRDUP'
+  libgit_sources += 'compat/strdup.c'
+endif
+
+if not compiler.has_function('strtoumax')
+  libgit_c_args += '-DNO_STRTOUMAX'
+  libgit_sources += [
+    'compat/strtoumax.c',
+    'compat/strtoimax.c',
+  ]
+endif
+
+if not compiler.has_function('strtoull')
+  libgit_c_args += '-DNO_STRTOULL'
+endif
+
+if not compiler.has_function('setenv')
+  libgit_c_args += '-DNO_SETENV'
+  libgit_sources += 'compat/setenv.c'
+endif
+
+if not compiler.has_function('qsort')
+  libgit_c_args += '-DINTERNAL_QSORT'
+endif
+libgit_sources += 'compat/qsort_s.c'
+
+# unsetenv is provided by compat/mingw.c.
+if host_machine.system() != 'windows' and not compiler.has_function('unsetenv')
+  libgit_c_args += '-DNO_UNSETENV'
+  libgit_sources += 'compat/unsetenv.c'
+endif
+
+if not compiler.has_function('mkdtemp')
+  libgit_c_args += '-DNO_MKDTEMP'
+  libgit_sources += 'compat/mkdtemp.c'
+endif
+
+if not compiler.has_function('initgroups')
+  libgit_c_args += '-DNO_INITGROUPS'
+endif
+
+if compiler.has_function('getdelim')
+  libgit_c_args += '-DHAVE_GETDELIM'
+endif
+
+if host_machine.system() == 'windows'
+  libgit_c_args += '-DUSE_WIN32_MMAP'
+elif not compiler.has_function('mmap')
+  libgit_c_args += '-DNO_MMAP'
+  libgit_sources += 'compat/mmap.c'
+endif
+
+if compiler.has_function('clock_gettime')
+  libgit_c_args += '-DHAVE_CLOCK_GETTIME'
+endif
+
+if compiler.compiles('''
+  #include <time.h>
+
+  void func(void)
+  {
+    clockid_t id = CLOCK_MONOTONIC;
+  }
+''', name: 'monotonic clock')
+  libgit_c_args += '-DHAVE_CLOCK_MONOTONIC'
+endif
+
+if not compiler.compiles('''
+  #include <inttypes.h>
+
+  void func(void)
+  {
+    uintmax_t x = 0;
+  }
+''', name: 'uintmax_t')
+  libgit_c_args += '-DNO_UINTMAX_T'
+endif
+
+has_bsd_sysctl = false
+if compiler.has_header('sys/sysctl.h')
+  if compiler.compiles('''
+    #include <stddef.h>
+    #include <sys/sysctl.h>
+
+    void func(void)
+    {
+      int val, mib[2] = { 0 };
+      size_t len = sizeof(val);
+      sysctl(mib, 2, &val, &len, NULL, 0);
+    }
+  ''', name: 'BSD sysctl')
+    libgit_c_args += '-DHAVE_BSD_SYSCTL'
+    has_bsd_sysctl = true
+  endif
+endif
+
+if not meson.is_cross_build() and compiler.run('''
+  #include <stdio.h>
+
+  int main(int argc, const char **argv)
+  {
+    FILE *f = fopen(".", "r");
+    return f ? 0 : 1;
+  }
+''', name: 'fread reads directories').returncode() == 0
+  libgit_c_args += '-DFREAD_READS_DIRECTORIES'
+  libgit_sources += 'compat/fopen.c'
+endif
+
+if not meson.is_cross_build() and fs.exists('/dev/tty')
+  libgit_c_args += '-DHAVE_DEV_TTY'
+endif
+
+if get_option('openssl').enabled() and get_option('CommonCrypto').enabled()
+  error('Can only use one SSL backend')
+endif
+
+security_framework = dependency('Security', required: get_option('CommonCrypto').disable_auto_if(host_machine.system() != 'darwin'))
+core_foundation_framework = dependency('CoreFoundation', required: security_framework.found())
+if security_framework.found()
+  libgit_dependencies += security_framework
+  libgit_dependencies += core_foundation_framework
+  libgit_c_args += '-DAPPLE_COMMON_CRYPTO'
+endif
+
+# OpenSSL is required when requested via the 'openssl' feature or via one of
+# the SHA1/SHA256 backends.
+openssl_required = get_option('openssl').disable_auto_if(security_framework.found())
+if get_option('sha1_backend') == 'openssl' or get_option('sha256_backend') == 'openssl'
+  openssl_required = true
+endif
+
+openssl = dependency('openssl', required: openssl_required, default_options: ['default_library=static'])
+if openssl.found()
+  libgit_dependencies += openssl
+endif
+
+# We may not want to use OpenSSL for anything but our SHA1/SHA256 backends, so
+# we cannot just set NO_OPENSSL based on whether or not the library was found.
+if not openssl.found() or get_option('openssl').disabled()
+  libgit_c_args += '-DNO_OPENSSL'
+endif
+
+sha1_backend = get_option('sha1_backend')
+if sha1_backend == 'sha1dc'
+  libgit_c_args += '-DSHA1_DC'
+  libgit_c_args += '-DSHA1DC_NO_STANDARD_INCLUDES=1'
+  libgit_c_args += '-DSHA1DC_INIT_SAFE_HASH_DEFAULT=0'
+  libgit_c_args += '-DSHA1DC_CUSTOM_INCLUDE_SHA1_C="git-compat-util.h"'
+  libgit_c_args += '-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="git-compat-util.h"'
+
+  libgit_sources += [
+    'sha1dc_git.c',
+    'sha1dc/sha1.c',
+    'sha1dc/ubc_check.c',
+  ]
+elif sha1_backend == 'common-crypto'
+  libgit_c_args += '-DCOMMON_DIGEST_FOR_OPENSSL'
+  libgit_c_args += '-DSHA1_APPLE'
+  libgit_c_args += '-DSHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L'
+elif sha1_backend == 'openssl'
+  if not openssl.found()
+    openssl = dependency('openssl', required: true)
+  endif
+
+  libgit_c_args += '-DSHA1_OPENSSL'
+  # Apple CommonCrypto requires chunking
+elif sha1_backend == 'block'
+  libgit_c_args += '-DSHA1_BLK'
+  libgit_sources += 'block-sha1/sha1.c'
+else
+  error('Unhandled SHA1 backend ' + sha1_backend)
+endif
+
+sha256_backend = get_option('sha256_backend')
+if sha256_backend == 'openssl'
+  libgit_c_args += '-DSHA256_OPENSSL'
+elif sha256_backend == 'nettle'
+  nettle = dependency('nettle')
+  libgit_dependencies += nettle
+  libgit_c_args += '-DSHA256_NETTLE'
+elif sha256_backend == 'gcrypt'
+  gcrypt = dependency('gcrypt')
+  libgit_dependencies += gcrypt
+  libgit_c_args += '-DSHA256_GCRYPT'
+elif sha256_backend == 'block'
+  libgit_c_args += '-DSHA256_BLK'
+  libgit_sources += 'sha256/block/sha256.c'
+else
+  error('Unhandled SHA256 backend ' + sha256_backend)
+endif
+
+if compiler.has_header_symbol('stdlib.h', 'arc4random_buf')
+  libgit_c_args += '-DHAVE_ARC4RANDOM'
+elif compiler.has_header_symbol('bsd/stdlib.h', 'arc4random_buf')
+  libgit_c_args += '-DHAVE_ARC4RANDOM_BSD'
+elif compiler.has_function('getrandom', prefix: '#include <sys/random.h>')
+  libgit_c_args += '-DHAVE_GETRANDOM'
+elif compiler.has_function('getentropy', prefix: '#include <unistd.h>')
+  libgit_c_args += '-DHAVE_GETENTROPY'
+elif compiler.has_function('RtlGenRandom', prefix: '#include <windows.h>\n#include <ntsecapi.h>')
+  libgit_c_args += '-DHAVE_RTLGENRANDOM'
+elif openssl.found()
+  libgit_c_args += '-DHAVE_OPENSSL_CSPRNG'
+endif
+
+if get_option('runtime_prefix')
+  libgit_c_args += '-DRUNTIME_PREFIX'
+  build_options_config.set('RUNTIME_PREFIX', '1')
+
+  if compiler.has_header('mach-o/dyld.h')
+    libgit_c_args += '-DHAVE_NS_GET_EXECUTABLE_PATH'
+  endif
+
+  if has_bsd_sysctl and compiler.compiles('''
+    #include <sys/sysctl.h>
+
+    void func(void)
+    {
+      KERN_PROC_PATHNAME; KERN_PROC;
+    }
+  ''', name: 'BSD KERN_PROC_PATHNAME')
+    libgit_c_args += '-DHAVE_NS_GET_EXECUTABLE_PATH'
+  endif
+
+  if host_machine.system() == 'linux'
+    libgit_c_args += '-DPROCFS_EXECUTABLE_PATH="/proc/self/exe' + '"'
+  elif host_machine.system() == 'openbsd'
+    libgit_c_args += '-DPROCFS_EXECUTABLE_PATH="' + '/proc/curproc/file' + '"'
+  elif host_machine.system() == 'netbsd'
+    libgit_c_args += '-DPROCFS_EXECUTABLE_PATH="' + '/proc/curproc/exe' + '"'
+  endif
+
+  if host_machine.system() == 'windows' and compiler.compiles('''
+    #include <stdlib.h>
+
+    void func(void)
+    {
+      _wpgmptr;
+    }
+  ''', name: 'Win32 _wpgmptr')
+    libgit_c_args += '-DHAVE_WPGMPTR'
+  endif
+else
+  build_options_config.set('RUNTIME_PREFIX', '')
+endif
+
+foreach key, value : {
+  'DIFF': diff.full_path(),
+  'GIT_TEST_CMP': diff.full_path() + ' -u',
+  'GIT_TEST_GITPERLLIB': meson.project_build_root() / 'perl',
+  'GIT_TEST_MERGE_TOOLS_DIR': meson.project_source_root() / 'mergetools',
+  'GIT_TEST_POPATH': meson.project_source_root() / 'po',
+  'GIT_TEST_TEMPLATE_DIR': meson.project_build_root() / 'templates',
+  'GIT_TEST_TEXTDOMAINDIR': meson.project_build_root() / 'po',
+  'PAGER_ENV': get_option('pager_environment'),
+  'PERL_PATH': perl.found() ? perl.full_path() : '',
+  'PYTHON_PATH': python.found () ? python.full_path() : '',
+  'SHELL_PATH': shell.full_path(),
+  'TAR': tar.full_path(),
+  'TEST_OUTPUT_DIRECTORY': test_output_directory,
+  'TEST_SHELL_PATH': shell.full_path(),
+}
+  if value != '' and cygpath.found()
+    value = run_command(cygpath, value, check: true).stdout().strip()
+  endif
+  build_options_config.set_quoted(key, value)
+endforeach
+
+configure_file(
+  input: 'GIT-BUILD-OPTIONS.in',
+  output: 'GIT-BUILD-OPTIONS',
+  configuration: build_options_config,
+)
+
+version_def_h = custom_target(
+  command: [
+    shell,
+    meson.current_source_dir() / 'GIT-VERSION-GEN',
+    meson.current_source_dir(),
+    '@INPUT@',
+    '@OUTPUT@',
+  ],
+  input: meson.current_source_dir() / 'version-def.h.in',
+  output: 'version-def.h',
+  build_always_stale: true,
+)
+
+git_version_file = custom_target(
+  command: [
+    shell,
+    meson.current_source_dir() / 'GIT-VERSION-GEN',
+    meson.current_source_dir(),
+    '@INPUT@',
+    '@OUTPUT@',
+  ],
+  input: meson.current_source_dir() / 'GIT-VERSION-FILE.in',
+  output: 'GIT-VERSION-FILE',
+  build_always_stale: true,
+)
+
+# Build a separate library for "version.c" so that we do not have to rebuild
+# everything when the current Git commit changes.
+libgit_version_library = static_library('git-version',
+  sources: [
+    'version.c',
+    version_def_h,
+  ],
+  c_args: libgit_c_args,
+  dependencies: libgit_dependencies,
+  include_directories: libgit_include_directories,
+)
+
+libgit_library = static_library('git',
+  sources: libgit_sources,
+  c_args: libgit_c_args,
+  link_with: libgit_version_library,
+  dependencies: libgit_dependencies,
+  include_directories: libgit_include_directories,
+)
+
+libgit = declare_dependency(
+  compile_args: libgit_c_args,
+  link_with: libgit_library,
+  dependencies: libgit_dependencies,
+  include_directories: libgit_include_directories,
+)
+
+common_main_sources = ['common-main.c']
+common_main_link_args = [ ]
+if host_machine.system() == 'windows'
+  git_rc = custom_target(
+    command: [
+      shell,
+      meson.current_source_dir() / 'GIT-VERSION-GEN',
+      meson.current_source_dir(),
+      '@INPUT@',
+      '@OUTPUT@',
+    ],
+    input: meson.current_source_dir() / 'git.rc.in',
+    output: 'git.rc',
+    build_always_stale: true,
+  )
+
+  common_main_sources += import('windows').compile_resources(git_rc,
+    include_directories: [meson.current_source_dir()],
+  )
+  if compiler.get_argument_syntax() == 'gcc'
+    common_main_link_args += [
+      '-municode',
+      '-Wl,-nxcompat',
+      '-Wl,-dynamicbase',
+      '-Wl,-pic-executable,-e,mainCRTStartup',
+    ]
+  elif compiler.get_argument_syntax() == 'msvc'
+    common_main_link_args += [
+      '/ENTRY:wmainCRTStartup',
+      'invalidcontinue.obj',
+    ]
+  else
+    error('Unsupported compiler ' + compiler.get_id())
+  endif
+endif
+common_main_library = static_library('common-main',
+  sources: common_main_sources,
+  c_args: libgit_c_args,
+  dependencies: libgit_dependencies,
+  include_directories: libgit_include_directories,
+)
+common_main = declare_dependency(
+  link_with: common_main_library,
+  link_args: common_main_link_args,
+)
+
+bin_wrappers = [ ]
+test_dependencies = [ ]
+
+git = executable('git',
+  sources: builtin_sources + 'git.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+bin_wrappers += git
+
+test_dependencies += executable('git-daemon',
+  sources: 'daemon.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+test_dependencies += executable('git-sh-i18n--envsubst',
+  sources: 'sh-i18n--envsubst.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+bin_wrappers += executable('git-shell',
+  sources: 'shell.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+test_dependencies += executable('git-http-backend',
+  sources: 'http-backend.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+bin_wrappers += executable('scalar',
+  sources: 'scalar.c',
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+if get_option('curl').enabled()
+  curl_sources = [
+    'http.c',
+    'http-walker.c',
+  ]
+
+  git_remote_http = executable('git-remote-http',
+    sources: curl_sources + 'remote-curl.c',
+    dependencies: [libgit, common_main],
+    install: true,
+    install_dir: get_option('libexecdir') / 'git-core',
+  )
+  test_dependencies += git_remote_http
+
+  test_dependencies += executable('git-http-fetch',
+    sources: curl_sources + 'http-fetch.c',
+    dependencies: [libgit, common_main],
+    install: true,
+    install_dir: get_option('libexecdir') / 'git-core',
+  )
+
+  if expat.found()
+    test_dependencies += executable('git-http-push',
+      sources: curl_sources + 'http-push.c',
+      dependencies: [libgit, common_main],
+      install: true,
+      install_dir: get_option('libexecdir') / 'git-core',
+    )
+  endif
+
+  foreach alias : [ 'git-remote-https', 'git-remote-ftp', 'git-remote-ftps' ]
+    test_dependencies += executable(alias,
+      objects: git_remote_http.extract_all_objects(recursive: false),
+      dependencies: [libgit, common_main],
+    )
+
+    install_symlink(alias + executable_suffix,
+      install_dir: get_option('libexecdir') / 'git-core',
+      pointing_to: 'git-remote-http',
+    )
+  endforeach
+endif
+
+imap_send_sources = ['imap-send.c']
+if use_curl_for_imap_send
+  imap_send_sources += curl_sources
+endif
+
+test_dependencies += executable('git-imap-send',
+  sources: imap_send_sources,
+  dependencies: [libgit, common_main],
+  install: true,
+  install_dir: get_option('libexecdir') / 'git-core',
+)
+
+foreach alias : [ 'git-receive-pack', 'git-upload-archive', 'git-upload-pack' ]
+  bin_wrappers += executable(alias,
+    objects: git.extract_all_objects(recursive: false),
+    dependencies: [libgit, common_main],
+  )
+
+  install_symlink(alias + executable_suffix,
+    install_dir: get_option('libexecdir') / 'git-core',
+    pointing_to: 'git-remote-http',
+  )
+endforeach
+
+foreach symlink : [
+  'git',
+  'git-receive-pack',
+  'git-shell',
+  'git-upload-archive',
+  'git-upload-pack',
+  'scalar',
+]
+  install_symlink(symlink,
+    install_dir: get_option('bindir'),
+    pointing_to: fs.relative_to(get_option('libexecdir') / 'git-core' / symlink, get_option('bindir')),
+  )
+endforeach
+
+scripts_sh = [
+  'git-difftool--helper.sh',
+  'git-filter-branch.sh',
+  'git-merge-octopus.sh',
+  'git-merge-one-file.sh',
+  'git-merge-resolve.sh',
+  'git-mergetool--lib.sh',
+  'git-mergetool.sh',
+  'git-quiltimport.sh',
+  'git-request-pull.sh',
+  'git-sh-i18n.sh',
+  'git-sh-setup.sh',
+  'git-submodule.sh',
+  'git-web--browse.sh',
+]
+if perl_features_enabled
+  scripts_sh += 'git-instaweb.sh'
+endif
+
+foreach script : scripts_sh
+  test_dependencies += custom_target(
+    input: script,
+    output: fs.stem(script),
+    command: [
+      shell,
+      meson.project_source_root() / 'generate-script.sh',
+      '@INPUT@',
+      '@OUTPUT@',
+      meson.project_build_root() / 'GIT-BUILD-OPTIONS',
+    ],
+    install: true,
+    install_dir: get_option('libexecdir') / 'git-core',
+  )
+endforeach
+
+if perl_features_enabled
+  scripts_perl = [
+    'git-archimport.perl',
+    'git-cvsexportcommit.perl',
+    'git-cvsimport.perl',
+    'git-cvsserver.perl',
+    'git-send-email.perl',
+    'git-svn.perl',
+  ]
+
+  pathsep = ':'
+  if host_machine.system() == 'windows'
+    pathsep = ';'
+  endif
+
+  perl_header_template = 'perl/header_templates/fixed_prefix.template.pl'
+  if get_option('runtime_prefix')
+    perl_header_template = 'perl/header_templates/runtime_prefix.template.pl'
+  endif
+
+  perl_header = configure_file(
+    input: perl_header_template,
+    output: 'GIT-PERL-HEADER',
+    configuration: {
+      'GITEXECDIR_REL': get_option('libexecdir') / 'git-core',
+      'PERLLIBDIR_REL': get_option('datadir') / 'perl5',
+      'LOCALEDIR_REL': get_option('datadir') / 'locale',
+      'INSTLIBDIR': get_option('datadir') / 'perl5',
+      'PATHSEP': pathsep,
+    },
+  )
+
+  generate_perl_command = [
+    shell,
+    meson.project_source_root() / 'generate-perl.sh',
+    meson.project_build_root() / 'GIT-BUILD-OPTIONS',
+    git_version_file.full_path(),
+    perl_header,
+    '@INPUT@',
+    '@OUTPUT@',
+  ]
+
+  foreach script : scripts_perl
+    generated_script = custom_target(
+      input: script,
+      output: fs.stem(script),
+      command: generate_perl_command,
+      install: true,
+      install_dir: get_option('libexecdir') / 'git-core',
+      depends: [git_version_file],
+    )
+    test_dependencies += generated_script
+
+    if script == 'git-cvsserver.perl'
+      bin_wrappers += generated_script
+
+      install_symlink(fs.stem(script),
+        install_dir: get_option('bindir'),
+        pointing_to: fs.relative_to(get_option('libexecdir') / 'git-core' / fs.stem(script), get_option('bindir')),
+      )
+    endif
+  endforeach
+
+  subdir('perl')
+endif
+
+if python.found()
+  scripts_python = [
+    'git-p4.py'
+  ]
+
+  foreach script : scripts_python
+    fs.copyfile(script, fs.stem(script),
+      install: true,
+      install_dir: get_option('libexecdir') / 'git-core',
+    )
+  endforeach
+endif
+
+mergetools = [
+  'mergetools/araxis',
+  'mergetools/bc',
+  'mergetools/codecompare',
+  'mergetools/deltawalker',
+  'mergetools/diffmerge',
+  'mergetools/diffuse',
+  'mergetools/ecmerge',
+  'mergetools/emerge',
+  'mergetools/examdiff',
+  'mergetools/guiffy',
+  'mergetools/gvimdiff',
+  'mergetools/kdiff3',
+  'mergetools/kompare',
+  'mergetools/meld',
+  'mergetools/nvimdiff',
+  'mergetools/opendiff',
+  'mergetools/p4merge',
+  'mergetools/smerge',
+  'mergetools/tkdiff',
+  'mergetools/tortoisemerge',
+  'mergetools/vimdiff',
+  'mergetools/vscode',
+  'mergetools/winmerge',
+  'mergetools/xxdiff',
+]
+
+foreach mergetool : mergetools
+  install_data(mergetool, install_dir: get_option('libexecdir') / 'git-core' / 'mergetools')
+endforeach
+
+if intl.found()
+  subdir('po')
+endif
+subdir('contrib')
+subdir('gitweb')
+subdir('templates')
+
+# Everything but the bin-wrappers need to come before this target such that we
+# can properly set up test dependencies. The bin-wrappers themselves are set up
+# at configuration time, so these are fine.
+if get_option('tests')
+  subdir('t')
+endif
+
+subdir('bin-wrappers')
+if get_option('docs') != []
+  subdir('Documentation')
+endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000000000000000000000000000000000000..83d9d527a0667ef85858e19c7f78896ab4b68aca
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,83 @@
+# Configuration for how Git behaves at runtime.
+option('default_pager', type: 'string', value: 'less',
+  description: 'Fall-back pager.')
+option('default_editor', type: 'string', value: 'vi',
+  description: 'Fall-back editor.')
+option('gitconfig', type: 'string', value: '/etc/gitconfig',
+  description: 'Path to the global git configuration file.')
+option('gitattributes', type: 'string', value: '/etc/gitattributes',
+  description: 'Path to the global git attributes file.')
+option('pager_environment', type: 'string', value: 'LESS=FRX LV=-c',
+  description: 'Environment used when spawning the pager')
+option('runtime_prefix', type: 'boolean', value: false,
+  description: 'Resolve ancillary tooling and support files relative to the location of the runtime binary instead of hard-coding them into the binary.')
+
+# Features supported by Git.
+option('curl', type: 'feature', value: 'enabled',
+  description: 'Build helpers used to access remotes with the HTTP transport.')
+option('expat', type: 'feature', value: 'enabled',
+  description: 'Build helpers used to push to remotes with the HTTP transport.')
+option('gettext', type: 'feature', value: 'auto',
+  description: 'Build translation files.')
+option('iconv', type: 'feature', value: 'auto',
+  description: 'Support reencoding strings with different encodings.')
+option('pcre2', type: 'feature', value: 'enabled',
+  description: 'Support Perl-compatible regular expressions in e.g. git-grep(1).')
+option('perl', type: 'feature', value: 'auto',
+  description: 'Build tools written in Perl.')
+option('perl_cpan_fallback', type: 'boolean', value: true,
+  description: 'Install bundled copies of CPAN modules that serve as a fallback in case the modules are not available on the system.')
+option('python', type: 'feature', value: 'auto',
+  description: 'Build tools written in Python.')
+option('regex', type: 'feature', value: 'auto',
+  description: 'Use the system-provided regex library instead of the bundled one.')
+
+# HTTPS backends.
+option('openssl', type: 'feature', value: 'auto',
+  description: 'Support access to HTTPS remotes. OpenSSL may still be pulled in if configured as SHA1 or SHA256 backend.')
+option('CommonCrypto', type: 'feature', value: 'auto',
+  description: 'Build tools written in Python.')
+
+# Hashing backends.
+option('sha1_backend', type: 'combo', choices: ['openssl', 'block', 'sha1dc', 'common-crypto'], value: 'sha1dc',
+  description: 'The backend used for hashing objects with the SHA1 object format')
+option('sha256_backend', type: 'combo', choices: ['openssl', 'nettle', 'gcrypt', 'block'], value: 'block',
+  description: 'The backend used for hashing objects with the SHA256 object format')
+
+# Build tweaks.
+option('macos_use_homebrew_gettext', type: 'boolean', value: true,
+  description: 'Use gettext from Homebrew instead of the slightly-broken system-provided one.')
+
+# gitweb configuration.
+option('gitweb_config', type: 'string', value: 'gitweb_config.perl')
+option('gitweb_config_system', type: 'string', value: '/etc/gitweb.conf')
+option('gitweb_config_common', type: 'string', value: '/etc/gitweb-common.conf')
+option('gitweb_home_link_str', type: 'string', value: 'projects')
+option('gitweb_sitename', type: 'string', value: '')
+option('gitweb_projectroot', type: 'string', value: '/pub/git')
+option('gitweb_project_maxdepth', type: 'string', value: '2007')
+option('gitweb_export_ok', type: 'string', value: '')
+option('gitweb_strict_export', type: 'string', value: '')
+option('gitweb_base_url', type: 'string', value: '')
+option('gitweb_list', type: 'string', value: '')
+option('gitweb_hometext', type: 'string', value: 'indextext.html')
+option('gitweb_css', type: 'string', value: 'static/gitweb.css')
+option('gitweb_logo', type: 'string', value: 'static/git-logo.png')
+option('gitweb_favicon', type: 'string', value: 'static/git-favicon.png')
+option('gitweb_js', type: 'string', value: 'static/gitweb.js')
+option('gitweb_site_html_head_string', type: 'string', value: '')
+option('gitweb_site_header', type: 'string', value: '')
+option('gitweb_site_footer', type: 'string', value: '')
+option('highlight_bin', type: 'string', value: 'highlight')
+
+# Documentation.
+option('docs', type: 'array', choices: ['man', 'html'], value: [],
+  description: 'Which documenattion formats to build and install.')
+option('default_help_format', type: 'combo', choices: ['man', 'html'], value: 'man',
+  description: 'Default format used when executing git-help(1).')
+
+# Testing.
+option('tests', type: 'boolean', value: true,
+  description: 'Enable building tests. This requires Perl, but is separate from the "perl" option such that you can build tests without Perl features enabled.')
+option('test_output_directory', type: 'string',
+  description: 'Path to the directory used to store test outputs')
diff --git a/perl/FromCPAN/Mail/meson.build b/perl/FromCPAN/Mail/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..129cff161c585645c111af0386bafe136258e3db
--- /dev/null
+++ b/perl/FromCPAN/Mail/meson.build
@@ -0,0 +1,7 @@
+test_dependencies += custom_target(
+  input: 'Address.pm',
+  output: 'Address.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5/FromCPAN/Mail',
+)
diff --git a/perl/FromCPAN/meson.build b/perl/FromCPAN/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..4e7ea909df3593fb65facb4bf6a1c42e954d6f70
--- /dev/null
+++ b/perl/FromCPAN/meson.build
@@ -0,0 +1,9 @@
+test_dependencies += custom_target(
+  input: 'Error.pm',
+  output: 'Error.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5/FromCPAN',
+)
+
+subdir('Mail')
diff --git a/perl/Git/LoadCPAN/Mail/meson.build b/perl/Git/LoadCPAN/Mail/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..7da5b37adb278b4e13f41ea5ebc762bb4d78b64b
--- /dev/null
+++ b/perl/Git/LoadCPAN/Mail/meson.build
@@ -0,0 +1,7 @@
+test_dependencies += custom_target(
+  input: 'Address.pm',
+  output: 'Address.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5/Git/LoadCPAN/Mail',
+)
diff --git a/perl/Git/LoadCPAN/meson.build b/perl/Git/LoadCPAN/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..9468c073aeb5ae1980f88b4c7f2c7fe69355ea37
--- /dev/null
+++ b/perl/Git/LoadCPAN/meson.build
@@ -0,0 +1,9 @@
+test_dependencies += custom_target(
+  input: 'Error.pm',
+  output: 'Error.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5/Git/LoadCPAN',
+)
+
+subdir('Mail')
diff --git a/perl/Git/SVN/Memoize/meson.build b/perl/Git/SVN/Memoize/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..515ab3dd926618f6c46b030e1f283d637fdddedf
--- /dev/null
+++ b/perl/Git/SVN/Memoize/meson.build
@@ -0,0 +1,7 @@
+test_dependencies += custom_target(
+  input: 'YAML.pm',
+  output: 'YAML.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5/Git/SVN',
+)
diff --git a/perl/Git/SVN/meson.build b/perl/Git/SVN/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..8338531041d8e5cd4e8c2c8a0c3dcfc8b2d23cc9
--- /dev/null
+++ b/perl/Git/SVN/meson.build
@@ -0,0 +1,20 @@
+foreach source : [
+  'Editor.pm',
+  'Fetcher.pm',
+  'GlobSpec.pm',
+  'Log.pm',
+  'Migration.pm',
+  'Prompt.pm',
+  'Ra.pm',
+  'Utils.pm',
+]
+  test_dependencies += custom_target(
+    input: source,
+    output: source,
+    command: generate_perl_command,
+    install: true,
+    install_dir: get_option('datadir') / 'perl5/Git/SVN',
+  )
+endforeach
+
+subdir('Memoize')
diff --git a/perl/Git/meson.build b/perl/Git/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..259209d7302809b6c1d3249d9d21548515c31c77
--- /dev/null
+++ b/perl/Git/meson.build
@@ -0,0 +1,18 @@
+foreach source : [
+  'I18N.pm',
+  'IndexInfo.pm',
+  'LoadCPAN.pm',
+  'Packet.pm',
+  'SVN.pm',
+]
+  test_dependencies += custom_target(
+    input: source,
+    output: source,
+    command: generate_perl_command,
+    install: true,
+    install_dir: get_option('datadir') / 'perl5/Git',
+  )
+endforeach
+
+subdir('LoadCPAN')
+subdir('SVN')
diff --git a/perl/meson.build b/perl/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..c22d6f8a1a3266ded27c43c43c88620f9b4badcf
--- /dev/null
+++ b/perl/meson.build
@@ -0,0 +1,12 @@
+test_dependencies += custom_target(
+  input: 'Git.pm',
+  output: 'Git.pm',
+  command: generate_perl_command,
+  install: true,
+  install_dir: get_option('datadir') / 'perl5',
+)
+
+subdir('Git')
+if get_option('perl_cpan_fallback')
+  subdir('FromCPAN')
+endif
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..d7154b6395b2bf4a27110aad65aa1f51f979a1e9
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1,27 @@
+i18n = import('i18n')
+
+translations = i18n.gettext('git',
+  languages: [
+    'bg',
+    'ca',
+    'de',
+    'el',
+    'es',
+    'fr',
+    'id',
+    'is',
+    'it',
+    'ko',
+    'pl',
+    'pt_PT',
+    'ru',
+    'sv',
+    'tr',
+    'uk',
+    'vi',
+    'zh_CN',
+    'zh_TW',
+  ],
+  install: true,
+)
+test_dependencies += translations[0]
diff --git a/subprojects/.gitignore b/subprojects/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..63ea916ef5f30255aef0c0b0b63f37b0e1d99fb2
--- /dev/null
+++ b/subprojects/.gitignore
@@ -0,0 +1 @@
+/*/
diff --git a/subprojects/curl.wrap b/subprojects/curl.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..f7e384b85cf5ee98672e4409e1d30f8f6f0e5735
--- /dev/null
+++ b/subprojects/curl.wrap
@@ -0,0 +1,13 @@
+[wrap-file]
+directory = curl-8.10.1
+source_url = https://github.com/curl/curl/releases/download/curl-8_10_1/curl-8.10.1.tar.xz
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/curl_8.10.1-1/curl-8.10.1.tar.xz
+source_filename = curl-8.10.1.tar.xz
+source_hash = 73a4b0e99596a09fa5924a4fb7e4b995a85fda0d18a2c02ab9cf134bebce04ee
+patch_filename = curl_8.10.1-1_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/curl_8.10.1-1/get_patch
+patch_hash = 707c28f35fc9b0e8d68c0c2800712007612f922a31da9637ce706a2159f3ddd8
+wrapdb_version = 8.10.1-1
+
+[provide]
+dependency_names = libcurl
diff --git a/subprojects/expat.wrap b/subprojects/expat.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..2e0427dcfd144c22d8c0bfd333e3cf31f14855a7
--- /dev/null
+++ b/subprojects/expat.wrap
@@ -0,0 +1,13 @@
+[wrap-file]
+directory = expat-2.6.3
+source_url = https://github.com/libexpat/libexpat/releases/download/R_2_6_3/expat-2.6.3.tar.xz
+source_filename = expat-2.6.3.tar.bz2
+source_hash = 274db254a6979bde5aad404763a704956940e465843f2a9bd9ed7af22e2c0efc
+patch_filename = expat_2.6.3-1_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.6.3-1/get_patch
+patch_hash = cf017fbe105e31428b2768360bd9be39094df4e948a1e8d1c54b6f7c76460cb1
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/expat_2.6.3-1/expat-2.6.3.tar.bz2
+wrapdb_version = 2.6.3-1
+
+[provide]
+expat = expat_dep
diff --git a/subprojects/openssl.wrap b/subprojects/openssl.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..873d55106e9b83c487a88dfbb85eaf73429464d7
--- /dev/null
+++ b/subprojects/openssl.wrap
@@ -0,0 +1,15 @@
+[wrap-file]
+directory = openssl-3.0.8
+source_url = https://www.openssl.org/source/openssl-3.0.8.tar.gz
+source_filename = openssl-3.0.8.tar.gz
+source_hash = 6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e
+patch_filename = openssl_3.0.8-3_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/openssl_3.0.8-3/get_patch
+patch_hash = 300da189e106942347d61a4a4295aa2edbcf06184f8d13b4cee0bed9fb936963
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/openssl_3.0.8-3/openssl-3.0.8.tar.gz
+wrapdb_version = 3.0.8-3
+
+[provide]
+libcrypto = libcrypto_dep
+libssl = libssl_dep
+openssl = openssl_dep
diff --git a/subprojects/pcre2.wrap b/subprojects/pcre2.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..7e184472543bde1572b9e780ae29318fe01e72bc
--- /dev/null
+++ b/subprojects/pcre2.wrap
@@ -0,0 +1,16 @@
+[wrap-file]
+directory = pcre2-10.44
+source_url = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.44/pcre2-10.44.tar.bz2
+source_filename = pcre2-10.44.tar.bz2
+source_hash = d34f02e113cf7193a1ebf2770d3ac527088d485d4e047ed10e5d217c6ef5de96
+patch_filename = pcre2_10.44-2_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.44-2/get_patch
+patch_hash = 4336d422ee9043847e5e10dbbbd01940d4c9e5027f31ccdc33a7898a1ca94009
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/pcre2_10.44-2/pcre2-10.44.tar.bz2
+wrapdb_version = 10.44-2
+
+[provide]
+libpcre2-8 = libpcre2_8
+libpcre2-16 = libpcre2_16
+libpcre2-32 = libpcre2_32
+libpcre2-posix = libpcre2_posix
diff --git a/subprojects/zlib.wrap b/subprojects/zlib.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..aa14de17740728cecdb7431fb101e55e9b022273
--- /dev/null
+++ b/subprojects/zlib.wrap
@@ -0,0 +1,13 @@
+[wrap-file]
+directory = zlib-1.3.1
+source_url = http://zlib.net/fossils/zlib-1.3.1.tar.gz
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/zlib_1.3.1-1/zlib-1.3.1.tar.gz
+source_filename = zlib-1.3.1.tar.gz
+source_hash = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
+patch_filename = zlib_1.3.1-1_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/zlib_1.3.1-1/get_patch
+patch_hash = e79b98eb24a75392009cec6f99ca5cdca9881ff20bfa174e8b8926d5c7a47095
+wrapdb_version = 1.3.1-1
+
+[provide]
+zlib = zlib_dep
diff --git a/t/helper/meson.build b/t/helper/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..5e83884246edc7841738de5085f3255aa1fa3fbe
--- /dev/null
+++ b/t/helper/meson.build
@@ -0,0 +1,91 @@
+test_tool_sources = [
+  '../unit-tests/test-lib.c',
+  'test-advise.c',
+  'test-bitmap.c',
+  'test-bloom.c',
+  'test-bundle-uri.c',
+  'test-cache-tree.c',
+  'test-chmtime.c',
+  'test-config.c',
+  'test-crontab.c',
+  'test-csprng.c',
+  'test-date.c',
+  'test-delete-gpgsig.c',
+  'test-delta.c',
+  'test-dir-iterator.c',
+  'test-drop-caches.c',
+  'test-dump-cache-tree.c',
+  'test-dump-fsmonitor.c',
+  'test-dump-split-index.c',
+  'test-dump-untracked-cache.c',
+  'test-env-helper.c',
+  'test-example-tap.c',
+  'test-find-pack.c',
+  'test-fsmonitor-client.c',
+  'test-genrandom.c',
+  'test-genzeros.c',
+  'test-getcwd.c',
+  'test-hash-speed.c',
+  'test-hash.c',
+  'test-hashmap.c',
+  'test-hexdump.c',
+  'test-json-writer.c',
+  'test-lazy-init-name-hash.c',
+  'test-match-trees.c',
+  'test-mergesort.c',
+  'test-mktemp.c',
+  'test-online-cpus.c',
+  'test-pack-mtimes.c',
+  'test-parse-options.c',
+  'test-parse-pathspec-file.c',
+  'test-partial-clone.c',
+  'test-path-utils.c',
+  'test-pcre2-config.c',
+  'test-pkt-line.c',
+  'test-proc-receive.c',
+  'test-progress.c',
+  'test-reach.c',
+  'test-read-cache.c',
+  'test-read-graph.c',
+  'test-read-midx.c',
+  'test-ref-store.c',
+  'test-reftable.c',
+  'test-regex.c',
+  'test-repository.c',
+  'test-revision-walking.c',
+  'test-rot13-filter.c',
+  'test-run-command.c',
+  'test-scrap-cache-tree.c',
+  'test-serve-v2.c',
+  'test-sha1.c',
+  'test-sha256.c',
+  'test-sigchain.c',
+  'test-simple-ipc.c',
+  'test-string-list.c',
+  'test-submodule-config.c',
+  'test-submodule-nested-repo-config.c',
+  'test-submodule.c',
+  'test-subprocess.c',
+  'test-tool.c',
+  'test-trace2.c',
+  'test-truncate.c',
+  'test-userdiff.c',
+  'test-wildmatch.c',
+  'test-windows-named-pipe.c',
+  'test-write-cache.c',
+  'test-xml-encode.c',
+]
+
+test_tool = executable('test-tool',
+  sources: test_tool_sources,
+  dependencies: [libgit, common_main],
+)
+bin_wrappers += test_tool
+test_dependencies += test_tool
+
+test_fake_ssh = executable('test-fake-ssh',
+  sources: 'test-fake-ssh.c',
+  dependencies: [libgit, common_main],
+)
+bin_wrappers += test_fake_ssh
+test_dependencies += test_fake_ssh
diff --git a/t/meson.build b/t/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..9ec0b7fa34a033d508448dbd56ab2322a5106fd0
--- /dev/null
+++ b/t/meson.build
@@ -0,0 +1,1106 @@
+clar_test_suites = [
+  'unit-tests/ctype.c',
+  'unit-tests/strvec.c',
+]
+
+clar_sources = [
+  'unit-tests/clar/clar.c',
+  'unit-tests/unit-test.c',
+]
+
+clar_decls_h = custom_target(
+  input: clar_test_suites,
+  output: 'clar-decls.h',
+  command : [shell, meson.current_source_dir() + '/unit-tests/generate-clar-decls.sh', '@OUTPUT@', '@INPUT@'],
+  env: script_environment,
+)
+clar_sources += clar_decls_h
+
+clar_sources += custom_target(
+  input: clar_decls_h,
+  output: 'clar.suite',
+  feed: true,
+  capture: true,
+  command : [awk, '-f', meson.current_source_dir() + '/unit-tests/clar-generate.awk'],
+)
+
+clar_unit_tests = executable('unit-tests',
+  sources: clar_sources + clar_test_suites,
+  dependencies: [libgit, common_main],
+)
+test('unit-tests', clar_unit_tests)
+
+unit_test_programs = [
+  'unit-tests/t-example-decorate.c',
+  'unit-tests/t-hash.c',
+  'unit-tests/t-hashmap.c',
+  'unit-tests/t-mem-pool.c',
+  'unit-tests/t-oid-array.c',
+  'unit-tests/t-oidmap.c',
+  'unit-tests/t-oidtree.c',
+  'unit-tests/t-prio-queue.c',
+  'unit-tests/t-reftable-basics.c',
+  'unit-tests/t-reftable-block.c',
+  'unit-tests/t-reftable-merged.c',
+  'unit-tests/t-reftable-pq.c',
+  'unit-tests/t-reftable-reader.c',
+  'unit-tests/t-reftable-readwrite.c',
+  'unit-tests/t-reftable-record.c',
+  'unit-tests/t-reftable-stack.c',
+  'unit-tests/t-reftable-tree.c',
+  'unit-tests/t-strbuf.c',
+  'unit-tests/t-strcmp-offset.c',
+  'unit-tests/t-trailer.c',
+  'unit-tests/t-urlmatch-normalization.c',
+]
+
+foreach unit_test_program : unit_test_programs
+  unit_test_name = fs.stem(unit_test_program)
+  unit_test = executable(unit_test_name,
+    sources: [
+      'unit-tests/test-lib.c',
+      'unit-tests/lib-oid.c',
+      'unit-tests/lib-reftable.c',
+      unit_test_program,
+    ],
+    dependencies: [libgit, common_main],
+  )
+  test(unit_test_name, unit_test,
+    workdir: meson.current_source_dir(),
+    timeout: 0,
+  )
+endforeach
+
+subdir('helper')
+
+integration_tests = [
+  't0000-basic.sh',
+  't0001-init.sh',
+  't0002-gitfile.sh',
+  't0003-attributes.sh',
+  't0004-unwritable.sh',
+  't0005-signals.sh',
+  't0006-date.sh',
+  't0007-git-var.sh',
+  't0008-ignores.sh',
+  't0010-racy-git.sh',
+  't0012-help.sh',
+  't0013-sha1dc.sh',
+  't0014-alias.sh',
+  't0017-env-helper.sh',
+  't0018-advice.sh',
+  't0019-json-writer.sh',
+  't0020-crlf.sh',
+  't0021-conversion.sh',
+  't0022-crlf-rename.sh',
+  't0023-crlf-am.sh',
+  't0024-crlf-archive.sh',
+  't0025-crlf-renormalize.sh',
+  't0026-eol-config.sh',
+  't0027-auto-crlf.sh',
+  't0028-working-tree-encoding.sh',
+  't0029-core-unsetenvvars.sh',
+  't0030-stripspace.sh',
+  't0033-safe-directory.sh',
+  't0034-root-safe-directory.sh',
+  't0035-safe-bare-repository.sh',
+  't0040-parse-options.sh',
+  't0041-usage.sh',
+  't0050-filesystem.sh',
+  't0051-windows-named-pipe.sh',
+  't0052-simple-ipc.sh',
+  't0055-beyond-symlinks.sh',
+  't0056-git-C.sh',
+  't0060-path-utils.sh',
+  't0061-run-command.sh',
+  't0062-revision-walking.sh',
+  't0063-string-list.sh',
+  't0066-dir-iterator.sh',
+  't0067-parse_pathspec_file.sh',
+  't0068-for-each-repo.sh',
+  't0070-fundamental.sh',
+  't0071-sort.sh',
+  't0080-unit-test-output.sh',
+  't0081-find-pack.sh',
+  't0090-cache-tree.sh',
+  't0091-bugreport.sh',
+  't0092-diagnose.sh',
+  't0095-bloom.sh',
+  't0100-previous.sh',
+  't0101-at-syntax.sh',
+  't0200-gettext-basic.sh',
+  't0201-gettext-fallbacks.sh',
+  't0202-gettext-perl.sh',
+  't0203-gettext-setlocale-sanity.sh',
+  't0204-gettext-reencode-sanity.sh',
+  't0210-trace2-normal.sh',
+  't0211-trace2-perf.sh',
+  't0212-trace2-event.sh',
+  't0300-credentials.sh',
+  't0301-credential-cache.sh',
+  't0302-credential-store.sh',
+  't0303-credential-external.sh',
+  't0410-partial-clone.sh',
+  't0411-clone-from-partial.sh',
+  't0450-txt-doc-vs-help.sh',
+  't0500-progress-display.sh',
+  't0600-reffiles-backend.sh',
+  't0601-reffiles-pack-refs.sh',
+  't0602-reffiles-fsck.sh',
+  't0610-reftable-basics.sh',
+  't0611-reftable-httpd.sh',
+  't0612-reftable-jgit-compatibility.sh',
+  't0613-reftable-write-options.sh',
+  't1000-read-tree-m-3way.sh',
+  't1001-read-tree-m-2way.sh',
+  't1002-read-tree-m-u-2way.sh',
+  't1003-read-tree-prefix.sh',
+  't1004-read-tree-m-u-wf.sh',
+  't1005-read-tree-reset.sh',
+  't1006-cat-file.sh',
+  't1007-hash-object.sh',
+  't1008-read-tree-overlay.sh',
+  't1009-read-tree-new-index.sh',
+  't1010-mktree.sh',
+  't1011-read-tree-sparse-checkout.sh',
+  't1012-read-tree-df.sh',
+  't1013-read-tree-submodule.sh',
+  't1014-read-tree-confusing.sh',
+  't1015-read-index-unmerged.sh',
+  't1016-compatObjectFormat.sh',
+  't1020-subdirectory.sh',
+  't1021-rerere-in-workdir.sh',
+  't1022-read-tree-partial-clone.sh',
+  't1050-large.sh',
+  't1051-large-conversion.sh',
+  't1060-object-corruption.sh',
+  't1090-sparse-checkout-scope.sh',
+  't1091-sparse-checkout-builtin.sh',
+  't1092-sparse-checkout-compatibility.sh',
+  't1100-commit-tree-options.sh',
+  't1300-config.sh',
+  't1301-shared-repo.sh',
+  't1302-repo-version.sh',
+  't1303-wacky-config.sh',
+  't1304-default-acl.sh',
+  't1305-config-include.sh',
+  't1306-xdg-files.sh',
+  't1307-config-blob.sh',
+  't1308-config-set.sh',
+  't1309-early-config.sh',
+  't1310-config-default.sh',
+  't1350-config-hooks-path.sh',
+  't1400-update-ref.sh',
+  't1401-symbolic-ref.sh',
+  't1402-check-ref-format.sh',
+  't1403-show-ref.sh',
+  't1404-update-ref-errors.sh',
+  't1405-main-ref-store.sh',
+  't1406-submodule-ref-store.sh',
+  't1407-worktree-ref-store.sh',
+  't1408-packed-refs.sh',
+  't1409-avoid-packing-refs.sh',
+  't1410-reflog.sh',
+  't1411-reflog-show.sh',
+  't1412-reflog-loop.sh',
+  't1413-reflog-detach.sh',
+  't1414-reflog-walk.sh',
+  't1415-worktree-refs.sh',
+  't1416-ref-transaction-hooks.sh',
+  't1417-reflog-updateref.sh',
+  't1418-reflog-exists.sh',
+  't1419-exclude-refs.sh',
+  't1420-lost-found.sh',
+  't1430-bad-ref-name.sh',
+  't1450-fsck.sh',
+  't1451-fsck-buffer.sh',
+  't1460-refs-migrate.sh',
+  't1500-rev-parse.sh',
+  't1501-work-tree.sh',
+  't1502-rev-parse-parseopt.sh',
+  't1503-rev-parse-verify.sh',
+  't1504-ceiling-dirs.sh',
+  't1505-rev-parse-last.sh',
+  't1506-rev-parse-diagnosis.sh',
+  't1507-rev-parse-upstream.sh',
+  't1508-at-combinations.sh',
+  't1509-root-work-tree.sh',
+  't1510-repo-setup.sh',
+  't1511-rev-parse-caret.sh',
+  't1512-rev-parse-disambiguation.sh',
+  't1513-rev-parse-prefix.sh',
+  't1514-rev-parse-push.sh',
+  't1515-rev-parse-outside-repo.sh',
+  't1517-outside-repo.sh',
+  't1600-index.sh',
+  't1601-index-bogus.sh',
+  't1700-split-index.sh',
+  't1701-racy-split-index.sh',
+  't1800-hook.sh',
+  't2000-conflict-when-checking-files-out.sh',
+  't2002-checkout-cache-u.sh',
+  't2003-checkout-cache-mkdir.sh',
+  't2004-checkout-cache-temp.sh',
+  't2005-checkout-index-symlinks.sh',
+  't2006-checkout-index-basic.sh',
+  't2007-checkout-symlink.sh',
+  't2008-checkout-subdir.sh',
+  't2009-checkout-statinfo.sh',
+  't2010-checkout-ambiguous.sh',
+  't2011-checkout-invalid-head.sh',
+  't2012-checkout-last.sh',
+  't2013-checkout-submodule.sh',
+  't2014-checkout-switch.sh',
+  't2015-checkout-unborn.sh',
+  't2016-checkout-patch.sh',
+  't2017-checkout-orphan.sh',
+  't2018-checkout-branch.sh',
+  't2019-checkout-ambiguous-ref.sh',
+  't2020-checkout-detach.sh',
+  't2021-checkout-overwrite.sh',
+  't2022-checkout-paths.sh',
+  't2023-checkout-m.sh',
+  't2024-checkout-dwim.sh',
+  't2025-checkout-no-overlay.sh',
+  't2026-checkout-pathspec-file.sh',
+  't2027-checkout-track.sh',
+  't2030-unresolve-info.sh',
+  't2050-git-dir-relative.sh',
+  't2060-switch.sh',
+  't2070-restore.sh',
+  't2071-restore-patch.sh',
+  't2072-restore-pathspec-file.sh',
+  't2080-parallel-checkout-basics.sh',
+  't2081-parallel-checkout-collisions.sh',
+  't2082-parallel-checkout-attributes.sh',
+  't2100-update-cache-badpath.sh',
+  't2101-update-index-reupdate.sh',
+  't2102-update-index-symlinks.sh',
+  't2103-update-index-ignore-missing.sh',
+  't2104-update-index-skip-worktree.sh',
+  't2105-update-index-gitfile.sh',
+  't2106-update-index-assume-unchanged.sh',
+  't2107-update-index-basic.sh',
+  't2108-update-index-refresh-racy.sh',
+  't2200-add-update.sh',
+  't2201-add-update-typechange.sh',
+  't2202-add-addremove.sh',
+  't2203-add-intent.sh',
+  't2204-add-ignored.sh',
+  't2205-add-worktree-config.sh',
+  't2300-cd-to-toplevel.sh',
+  't2400-worktree-add.sh',
+  't2401-worktree-prune.sh',
+  't2402-worktree-list.sh',
+  't2403-worktree-move.sh',
+  't2404-worktree-config.sh',
+  't2405-worktree-submodule.sh',
+  't2406-worktree-repair.sh',
+  't2407-worktree-heads.sh',
+  't2408-worktree-relative.sh',
+  't2500-untracked-overwriting.sh',
+  't2501-cwd-empty.sh',
+  't3000-ls-files-others.sh',
+  't3001-ls-files-others-exclude.sh',
+  't3002-ls-files-dashpath.sh',
+  't3003-ls-files-exclude.sh',
+  't3004-ls-files-basic.sh',
+  't3005-ls-files-relative.sh',
+  't3006-ls-files-long.sh',
+  't3007-ls-files-recurse-submodules.sh',
+  't3008-ls-files-lazy-init-name-hash.sh',
+  't3009-ls-files-others-nonsubmodule.sh',
+  't3010-ls-files-killed-modified.sh',
+  't3011-common-prefixes-and-directory-traversal.sh',
+  't3012-ls-files-dedup.sh',
+  't3013-ls-files-format.sh',
+  't3020-ls-files-error-unmatch.sh',
+  't3040-subprojects-basic.sh',
+  't3050-subprojects-fetch.sh',
+  't3060-ls-files-with-tree.sh',
+  't3070-wildmatch.sh',
+  't3100-ls-tree-restrict.sh',
+  't3101-ls-tree-dirname.sh',
+  't3102-ls-tree-wildcards.sh',
+  't3103-ls-tree-misc.sh',
+  't3104-ls-tree-format.sh',
+  't3105-ls-tree-output.sh',
+  't3200-branch.sh',
+  't3201-branch-contains.sh',
+  't3202-show-branch.sh',
+  't3203-branch-output.sh',
+  't3204-branch-name-interpretation.sh',
+  't3205-branch-color.sh',
+  't3206-range-diff.sh',
+  't3207-branch-submodule.sh',
+  't3211-peel-ref.sh',
+  't3300-funny-names.sh',
+  't3301-notes.sh',
+  't3302-notes-index-expensive.sh',
+  't3303-notes-subtrees.sh',
+  't3304-notes-mixed.sh',
+  't3305-notes-fanout.sh',
+  't3306-notes-prune.sh',
+  't3307-notes-man.sh',
+  't3308-notes-merge.sh',
+  't3309-notes-merge-auto-resolve.sh',
+  't3310-notes-merge-manual-resolve.sh',
+  't3311-notes-merge-fanout.sh',
+  't3320-notes-merge-worktrees.sh',
+  't3321-notes-stripspace.sh',
+  't3400-rebase.sh',
+  't3401-rebase-and-am-rename.sh',
+  't3402-rebase-merge.sh',
+  't3403-rebase-skip.sh',
+  't3404-rebase-interactive.sh',
+  't3405-rebase-malformed.sh',
+  't3406-rebase-message.sh',
+  't3407-rebase-abort.sh',
+  't3408-rebase-multi-line.sh',
+  't3409-rebase-environ.sh',
+  't3412-rebase-root.sh',
+  't3413-rebase-hook.sh',
+  't3415-rebase-autosquash.sh',
+  't3416-rebase-onto-threedots.sh',
+  't3417-rebase-whitespace-fix.sh',
+  't3418-rebase-continue.sh',
+  't3419-rebase-patch-id.sh',
+  't3420-rebase-autostash.sh',
+  't3421-rebase-topology-linear.sh',
+  't3422-rebase-incompatible-options.sh',
+  't3423-rebase-reword.sh',
+  't3424-rebase-empty.sh',
+  't3425-rebase-topology-merges.sh',
+  't3426-rebase-submodule.sh',
+  't3427-rebase-subtree.sh',
+  't3428-rebase-signoff.sh',
+  't3429-rebase-edit-todo.sh',
+  't3430-rebase-merges.sh',
+  't3431-rebase-fork-point.sh',
+  't3432-rebase-fast-forward.sh',
+  't3433-rebase-across-mode-change.sh',
+  't3434-rebase-i18n.sh',
+  't3435-rebase-gpg-sign.sh',
+  't3436-rebase-more-options.sh',
+  't3437-rebase-fixup-options.sh',
+  't3438-rebase-broken-files.sh',
+  't3500-cherry.sh',
+  't3501-revert-cherry-pick.sh',
+  't3502-cherry-pick-merge.sh',
+  't3503-cherry-pick-root.sh',
+  't3504-cherry-pick-rerere.sh',
+  't3505-cherry-pick-empty.sh',
+  't3506-cherry-pick-ff.sh',
+  't3507-cherry-pick-conflict.sh',
+  't3508-cherry-pick-many-commits.sh',
+  't3509-cherry-pick-merge-df.sh',
+  't3510-cherry-pick-sequence.sh',
+  't3511-cherry-pick-x.sh',
+  't3512-cherry-pick-submodule.sh',
+  't3513-revert-submodule.sh',
+  't3514-cherry-pick-revert-gpg.sh',
+  't3600-rm.sh',
+  't3601-rm-pathspec-file.sh',
+  't3602-rm-sparse-checkout.sh',
+  't3650-replay-basics.sh',
+  't3700-add.sh',
+  't3701-add-interactive.sh',
+  't3702-add-edit.sh',
+  't3703-add-magic-pathspec.sh',
+  't3704-add-pathspec-file.sh',
+  't3705-add-sparse-checkout.sh',
+  't3800-mktag.sh',
+  't3900-i18n-commit.sh',
+  't3901-i18n-patch.sh',
+  't3902-quoted.sh',
+  't3903-stash.sh',
+  't3904-stash-patch.sh',
+  't3905-stash-include-untracked.sh',
+  't3906-stash-submodule.sh',
+  't3907-stash-show-config.sh',
+  't3908-stash-in-worktree.sh',
+  't3909-stash-pathspec-file.sh',
+  't3910-mac-os-precompose.sh',
+  't3920-crlf-messages.sh',
+  't4000-diff-format.sh',
+  't4001-diff-rename.sh',
+  't4002-diff-basic.sh',
+  't4003-diff-rename-1.sh',
+  't4004-diff-rename-symlink.sh',
+  't4005-diff-rename-2.sh',
+  't4006-diff-mode.sh',
+  't4007-rename-3.sh',
+  't4008-diff-break-rewrite.sh',
+  't4009-diff-rename-4.sh',
+  't4010-diff-pathspec.sh',
+  't4011-diff-symlink.sh',
+  't4012-diff-binary.sh',
+  't4013-diff-various.sh',
+  't4014-format-patch.sh',
+  't4015-diff-whitespace.sh',
+  't4016-diff-quote.sh',
+  't4017-diff-retval.sh',
+  't4018-diff-funcname.sh',
+  't4019-diff-wserror.sh',
+  't4020-diff-external.sh',
+  't4021-format-patch-numbered.sh',
+  't4022-diff-rewrite.sh',
+  't4023-diff-rename-typechange.sh',
+  't4024-diff-optimize-common.sh',
+  't4025-hunk-header.sh',
+  't4026-color.sh',
+  't4027-diff-submodule.sh',
+  't4028-format-patch-mime-headers.sh',
+  't4029-diff-trailing-space.sh',
+  't4030-diff-textconv.sh',
+  't4031-diff-rewrite-binary.sh',
+  't4032-diff-inter-hunk-context.sh',
+  't4033-diff-patience.sh',
+  't4034-diff-words.sh',
+  't4035-diff-quiet.sh',
+  't4036-format-patch-signer-mime.sh',
+  't4037-diff-r-t-dirs.sh',
+  't4038-diff-combined.sh',
+  't4039-diff-assume-unchanged.sh',
+  't4040-whitespace-status.sh',
+  't4041-diff-submodule-option.sh',
+  't4042-diff-textconv-caching.sh',
+  't4043-diff-rename-binary.sh',
+  't4044-diff-index-unique-abbrev.sh',
+  't4045-diff-relative.sh',
+  't4046-diff-unmerged.sh',
+  't4047-diff-dirstat.sh',
+  't4048-diff-combined-binary.sh',
+  't4049-diff-stat-count.sh',
+  't4050-diff-histogram.sh',
+  't4051-diff-function-context.sh',
+  't4052-stat-output.sh',
+  't4053-diff-no-index.sh',
+  't4054-diff-bogus-tree.sh',
+  't4055-diff-context.sh',
+  't4056-diff-order.sh',
+  't4057-diff-combined-paths.sh',
+  't4058-diff-duplicates.sh',
+  't4059-diff-submodule-not-initialized.sh',
+  't4060-diff-submodule-option-diff-format.sh',
+  't4061-diff-indent.sh',
+  't4062-diff-pickaxe.sh',
+  't4063-diff-blobs.sh',
+  't4064-diff-oidfind.sh',
+  't4065-diff-anchored.sh',
+  't4066-diff-emit-delay.sh',
+  't4067-diff-partial-clone.sh',
+  't4068-diff-symmetric-merge-base.sh',
+  't4069-remerge-diff.sh',
+  't4100-apply-stat.sh',
+  't4101-apply-nonl.sh',
+  't4102-apply-rename.sh',
+  't4103-apply-binary.sh',
+  't4104-apply-boundary.sh',
+  't4105-apply-fuzz.sh',
+  't4106-apply-stdin.sh',
+  't4107-apply-ignore-whitespace.sh',
+  't4108-apply-threeway.sh',
+  't4109-apply-multifrag.sh',
+  't4110-apply-scan.sh',
+  't4111-apply-subdir.sh',
+  't4112-apply-renames.sh',
+  't4113-apply-ending.sh',
+  't4114-apply-typechange.sh',
+  't4115-apply-symlink.sh',
+  't4116-apply-reverse.sh',
+  't4117-apply-reject.sh',
+  't4118-apply-empty-context.sh',
+  't4119-apply-config.sh',
+  't4120-apply-popt.sh',
+  't4121-apply-diffs.sh',
+  't4122-apply-symlink-inside.sh',
+  't4123-apply-shrink.sh',
+  't4124-apply-ws-rule.sh',
+  't4125-apply-ws-fuzz.sh',
+  't4126-apply-empty.sh',
+  't4127-apply-same-fn.sh',
+  't4128-apply-root.sh',
+  't4129-apply-samemode.sh',
+  't4130-apply-criss-cross-rename.sh',
+  't4131-apply-fake-ancestor.sh',
+  't4132-apply-removal.sh',
+  't4133-apply-filenames.sh',
+  't4134-apply-submodule.sh',
+  't4135-apply-weird-filenames.sh',
+  't4136-apply-check.sh',
+  't4137-apply-submodule.sh',
+  't4138-apply-ws-expansion.sh',
+  't4139-apply-escape.sh',
+  't4140-apply-ita.sh',
+  't4141-apply-too-large.sh',
+  't4150-am.sh',
+  't4151-am-abort.sh',
+  't4152-am-subjects.sh',
+  't4153-am-resume-override-opts.sh',
+  't4200-rerere.sh',
+  't4201-shortlog.sh',
+  't4202-log.sh',
+  't4203-mailmap.sh',
+  't4204-patch-id.sh',
+  't4205-log-pretty-formats.sh',
+  't4206-log-follow-harder-copies.sh',
+  't4207-log-decoration-colors.sh',
+  't4208-log-magic-pathspec.sh',
+  't4209-log-pickaxe.sh',
+  't4210-log-i18n.sh',
+  't4211-line-log.sh',
+  't4212-log-corrupt.sh',
+  't4213-log-tabexpand.sh',
+  't4214-log-graph-octopus.sh',
+  't4215-log-skewed-merges.sh',
+  't4216-log-bloom.sh',
+  't4217-log-limit.sh',
+  't4252-am-options.sh',
+  't4253-am-keep-cr-dos.sh',
+  't4254-am-corrupt.sh',
+  't4255-am-submodule.sh',
+  't4256-am-format-flowed.sh',
+  't4257-am-interactive.sh',
+  't4258-am-quoted-cr.sh',
+  't4300-merge-tree.sh',
+  't4301-merge-tree-write-tree.sh',
+  't5000-tar-tree.sh',
+  't5001-archive-attr.sh',
+  't5002-archive-attr-pattern.sh',
+  't5003-archive-zip.sh',
+  't5004-archive-corner-cases.sh',
+  't5100-mailinfo.sh',
+  't5150-request-pull.sh',
+  't5200-update-server-info.sh',
+  't5300-pack-object.sh',
+  't5301-sliding-window.sh',
+  't5302-pack-index.sh',
+  't5303-pack-corruption-resilience.sh',
+  't5304-prune.sh',
+  't5305-include-tag.sh',
+  't5306-pack-nobase.sh',
+  't5307-pack-missing-commit.sh',
+  't5308-pack-detect-duplicates.sh',
+  't5309-pack-delta-cycles.sh',
+  't5310-pack-bitmaps.sh',
+  't5311-pack-bitmaps-shallow.sh',
+  't5312-prune-corruption.sh',
+  't5313-pack-bounds-checks.sh',
+  't5314-pack-cycle-detection.sh',
+  't5315-pack-objects-compression.sh',
+  't5316-pack-delta-depth.sh',
+  't5317-pack-objects-filter-objects.sh',
+  't5318-commit-graph.sh',
+  't5319-multi-pack-index.sh',
+  't5320-delta-islands.sh',
+  't5321-pack-large-objects.sh',
+  't5322-pack-objects-sparse.sh',
+  't5323-pack-redundant.sh',
+  't5324-split-commit-graph.sh',
+  't5325-reverse-index.sh',
+  't5326-multi-pack-bitmaps.sh',
+  't5327-multi-pack-bitmaps-rev.sh',
+  't5328-commit-graph-64bit-time.sh',
+  't5329-pack-objects-cruft.sh',
+  't5330-no-lazy-fetch-with-commit-graph.sh',
+  't5331-pack-objects-stdin.sh',
+  't5332-multi-pack-reuse.sh',
+  't5333-pseudo-merge-bitmaps.sh',
+  't5334-incremental-multi-pack-index.sh',
+  't5351-unpack-large-objects.sh',
+  't5400-send-pack.sh',
+  't5401-update-hooks.sh',
+  't5402-post-merge-hook.sh',
+  't5403-post-checkout-hook.sh',
+  't5404-tracking-branches.sh',
+  't5405-send-pack-rewind.sh',
+  't5406-remote-rejects.sh',
+  't5407-post-rewrite-hook.sh',
+  't5408-send-pack-stdin.sh',
+  't5409-colorize-remote-messages.sh',
+  't5410-receive-pack-alternates.sh',
+  't5411-proc-receive-hook.sh',
+  't5500-fetch-pack.sh',
+  't5501-fetch-push-alternates.sh',
+  't5502-quickfetch.sh',
+  't5503-tagfollow.sh',
+  't5504-fetch-receive-strict.sh',
+  't5505-remote.sh',
+  't5506-remote-groups.sh',
+  't5507-remote-environment.sh',
+  't5509-fetch-push-namespaces.sh',
+  't5510-fetch.sh',
+  't5511-refspec.sh',
+  't5512-ls-remote.sh',
+  't5513-fetch-track.sh',
+  't5514-fetch-multiple.sh',
+  't5515-fetch-merge-logic.sh',
+  't5516-fetch-push.sh',
+  't5517-push-mirror.sh',
+  't5518-fetch-exit-status.sh',
+  't5519-push-alternates.sh',
+  't5520-pull.sh',
+  't5521-pull-options.sh',
+  't5522-pull-symlink.sh',
+  't5523-push-upstream.sh',
+  't5524-pull-msg.sh',
+  't5525-fetch-tagopt.sh',
+  't5526-fetch-submodules.sh',
+  't5527-fetch-odd-refs.sh',
+  't5528-push-default.sh',
+  't5529-push-errors.sh',
+  't5530-upload-pack-error.sh',
+  't5531-deep-submodule-push.sh',
+  't5532-fetch-proxy.sh',
+  't5533-push-cas.sh',
+  't5534-push-signed.sh',
+  't5535-fetch-push-symref.sh',
+  't5536-fetch-conflicts.sh',
+  't5537-fetch-shallow.sh',
+  't5538-push-shallow.sh',
+  't5539-fetch-http-shallow.sh',
+  't5540-http-push-webdav.sh',
+  't5541-http-push-smart.sh',
+  't5542-push-http-shallow.sh',
+  't5543-atomic-push.sh',
+  't5544-pack-objects-hook.sh',
+  't5545-push-options.sh',
+  't5546-receive-limits.sh',
+  't5547-push-quarantine.sh',
+  't5548-push-porcelain.sh',
+  't5549-fetch-push-http.sh',
+  't5550-http-fetch-dumb.sh',
+  't5551-http-fetch-smart.sh',
+  't5552-skipping-fetch-negotiator.sh',
+  't5553-set-upstream.sh',
+  't5554-noop-fetch-negotiator.sh',
+  't5555-http-smart-common.sh',
+  't5557-http-get.sh',
+  't5558-clone-bundle-uri.sh',
+  't5559-http-fetch-smart-http2.sh',
+  't5560-http-backend-noserver.sh',
+  't5561-http-backend.sh',
+  't5562-http-backend-content-length.sh',
+  't5563-simple-http-auth.sh',
+  't5564-http-proxy.sh',
+  't5570-git-daemon.sh',
+  't5571-pre-push-hook.sh',
+  't5572-pull-submodule.sh',
+  't5573-pull-verify-signatures.sh',
+  't5574-fetch-output.sh',
+  't5580-unc-paths.sh',
+  't5581-http-curl-verbose.sh',
+  't5582-fetch-negative-refspec.sh',
+  't5583-push-branches.sh',
+  't5600-clone-fail-cleanup.sh',
+  't5601-clone.sh',
+  't5602-clone-remote-exec.sh',
+  't5603-clone-dirname.sh',
+  't5604-clone-reference.sh',
+  't5605-clone-local.sh',
+  't5606-clone-options.sh',
+  't5607-clone-bundle.sh',
+  't5608-clone-2gb.sh',
+  't5609-clone-branch.sh',
+  't5610-clone-detached.sh',
+  't5611-clone-config.sh',
+  't5612-clone-refspec.sh',
+  't5613-info-alternate.sh',
+  't5614-clone-submodules-shallow.sh',
+  't5615-alternate-env.sh',
+  't5616-partial-clone.sh',
+  't5617-clone-submodules-remote.sh',
+  't5618-alternate-refs.sh',
+  't5619-clone-local-ambiguous-transport.sh',
+  't5700-protocol-v1.sh',
+  't5701-git-serve.sh',
+  't5702-protocol-v2.sh',
+  't5703-upload-pack-ref-in-want.sh',
+  't5704-protocol-violations.sh',
+  't5705-session-id-in-capabilities.sh',
+  't5730-protocol-v2-bundle-uri-file.sh',
+  't5731-protocol-v2-bundle-uri-git.sh',
+  't5732-protocol-v2-bundle-uri-http.sh',
+  't5750-bundle-uri-parse.sh',
+  't5801-remote-helpers.sh',
+  't5802-connect-helper.sh',
+  't5810-proto-disable-local.sh',
+  't5811-proto-disable-git.sh',
+  't5812-proto-disable-http.sh',
+  't5813-proto-disable-ssh.sh',
+  't5814-proto-disable-ext.sh',
+  't5815-submodule-protos.sh',
+  't5900-repo-selection.sh',
+  't6000-rev-list-misc.sh',
+  't6001-rev-list-graft.sh',
+  't6002-rev-list-bisect.sh',
+  't6003-rev-list-topo-order.sh',
+  't6004-rev-list-path-optim.sh',
+  't6005-rev-list-count.sh',
+  't6006-rev-list-format.sh',
+  't6007-rev-list-cherry-pick-file.sh',
+  't6008-rev-list-submodule.sh',
+  't6009-rev-list-parent.sh',
+  't6010-merge-base.sh',
+  't6011-rev-list-with-bad-commit.sh',
+  't6012-rev-list-simplify.sh',
+  't6013-rev-list-reverse-parents.sh',
+  't6014-rev-list-all.sh',
+  't6016-rev-list-graph-simplify-history.sh',
+  't6017-rev-list-stdin.sh',
+  't6018-rev-list-glob.sh',
+  't6019-rev-list-ancestry-path.sh',
+  't6020-bundle-misc.sh',
+  't6021-rev-list-exclude-hidden.sh',
+  't6022-rev-list-missing.sh',
+  't6030-bisect-porcelain.sh',
+  't6040-tracking-info.sh',
+  't6041-bisect-submodule.sh',
+  't6050-replace.sh',
+  't6060-merge-index.sh',
+  't6100-rev-list-in-order.sh',
+  't6101-rev-parse-parents.sh',
+  't6102-rev-list-unexpected-objects.sh',
+  't6110-rev-list-sparse.sh',
+  't6111-rev-list-treesame.sh',
+  't6112-rev-list-filters-objects.sh',
+  't6113-rev-list-bitmap-filters.sh',
+  't6114-keep-packs.sh',
+  't6115-rev-list-du.sh',
+  't6120-describe.sh',
+  't6130-pathspec-noglob.sh',
+  't6131-pathspec-icase.sh',
+  't6132-pathspec-exclude.sh',
+  't6133-pathspec-rev-dwim.sh',
+  't6134-pathspec-in-submodule.sh',
+  't6135-pathspec-with-attrs.sh',
+  't6136-pathspec-in-bare.sh',
+  't6200-fmt-merge-msg.sh',
+  't6300-for-each-ref.sh',
+  't6301-for-each-ref-errors.sh',
+  't6302-for-each-ref-filter.sh',
+  't6400-merge-df.sh',
+  't6401-merge-criss-cross.sh',
+  't6402-merge-rename.sh',
+  't6403-merge-file.sh',
+  't6404-recursive-merge.sh',
+  't6405-merge-symlinks.sh',
+  't6406-merge-attr.sh',
+  't6407-merge-binary.sh',
+  't6408-merge-up-to-date.sh',
+  't6409-merge-subtree.sh',
+  't6411-merge-filemode.sh',
+  't6412-merge-large-rename.sh',
+  't6413-merge-crlf.sh',
+  't6414-merge-rename-nocruft.sh',
+  't6415-merge-dir-to-symlink.sh',
+  't6416-recursive-corner-cases.sh',
+  't6417-merge-ours-theirs.sh',
+  't6418-merge-text-auto.sh',
+  't6419-merge-ignorecase.sh',
+  't6421-merge-partial-clone.sh',
+  't6422-merge-rename-corner-cases.sh',
+  't6423-merge-rename-directories.sh',
+  't6424-merge-unrelated-index-changes.sh',
+  't6425-merge-rename-delete.sh',
+  't6426-merge-skip-unneeded-updates.sh',
+  't6427-diff3-conflict-markers.sh',
+  't6428-merge-conflicts-sparse.sh',
+  't6429-merge-sequence-rename-caching.sh',
+  't6430-merge-recursive.sh',
+  't6431-merge-criscross.sh',
+  't6432-merge-recursive-space-options.sh',
+  't6433-merge-toplevel.sh',
+  't6434-merge-recursive-rename-options.sh',
+  't6435-merge-sparse.sh',
+  't6436-merge-overwrite.sh',
+  't6437-submodule-merge.sh',
+  't6438-submodule-directory-file-conflicts.sh',
+  't6439-merge-co-error-msgs.sh',
+  't6500-gc.sh',
+  't6501-freshen-objects.sh',
+  't6600-test-reach.sh',
+  't6700-tree-depth.sh',
+  't7001-mv.sh',
+  't7002-mv-sparse-checkout.sh',
+  't7003-filter-branch.sh',
+  't7004-tag.sh',
+  't7005-editor.sh',
+  't7006-pager.sh',
+  't7007-show.sh',
+  't7008-filter-branch-null-sha1.sh',
+  't7010-setup.sh',
+  't7011-skip-worktree-reading.sh',
+  't7012-skip-worktree-writing.sh',
+  't7030-verify-tag.sh',
+  't7031-verify-tag-signed-ssh.sh',
+  't7060-wtstatus.sh',
+  't7061-wtstatus-ignore.sh',
+  't7062-wtstatus-ignorecase.sh',
+  't7063-status-untracked-cache.sh',
+  't7064-wtstatus-pv2.sh',
+  't7101-reset-empty-subdirs.sh',
+  't7102-reset.sh',
+  't7103-reset-bare.sh',
+  't7104-reset-hard.sh',
+  't7105-reset-patch.sh',
+  't7106-reset-unborn-branch.sh',
+  't7107-reset-pathspec-file.sh',
+  't7110-reset-merge.sh',
+  't7111-reset-table.sh',
+  't7112-reset-submodule.sh',
+  't7113-post-index-change-hook.sh',
+  't7201-co.sh',
+  't7300-clean.sh',
+  't7301-clean-interactive.sh',
+  't7400-submodule-basic.sh',
+  't7401-submodule-summary.sh',
+  't7402-submodule-rebase.sh',
+  't7403-submodule-sync.sh',
+  't7406-submodule-update.sh',
+  't7407-submodule-foreach.sh',
+  't7408-submodule-reference.sh',
+  't7409-submodule-detached-work-tree.sh',
+  't7411-submodule-config.sh',
+  't7412-submodule-absorbgitdirs.sh',
+  't7413-submodule-is-active.sh',
+  't7414-submodule-mistakes.sh',
+  't7416-submodule-dash-url.sh',
+  't7417-submodule-path-url.sh',
+  't7418-submodule-sparse-gitmodules.sh',
+  't7419-submodule-set-branch.sh',
+  't7420-submodule-set-url.sh',
+  't7421-submodule-summary-add.sh',
+  't7422-submodule-output.sh',
+  't7423-submodule-symlinks.sh',
+  't7424-submodule-mixed-ref-formats.sh',
+  't7450-bad-git-dotfiles.sh',
+  't7500-commit-template-squash-signoff.sh',
+  't7501-commit-basic-functionality.sh',
+  't7502-commit-porcelain.sh',
+  't7503-pre-commit-and-pre-merge-commit-hooks.sh',
+  't7504-commit-msg-hook.sh',
+  't7505-prepare-commit-msg-hook.sh',
+  't7506-status-submodule.sh',
+  't7507-commit-verbose.sh',
+  't7508-status.sh',
+  't7509-commit-authorship.sh',
+  't7510-signed-commit.sh',
+  't7511-status-index.sh',
+  't7512-status-help.sh',
+  't7513-interpret-trailers.sh',
+  't7514-commit-patch.sh',
+  't7515-status-symlinks.sh',
+  't7516-commit-races.sh',
+  't7517-per-repo-email.sh',
+  't7518-ident-corner-cases.sh',
+  't7519-status-fsmonitor.sh',
+  't7520-ignored-hook-warning.sh',
+  't7521-ignored-mode.sh',
+  't7524-commit-summary.sh',
+  't7525-status-rename.sh',
+  't7526-commit-pathspec-file.sh',
+  't7527-builtin-fsmonitor.sh',
+  't7528-signed-commit-ssh.sh',
+  't7600-merge.sh',
+  't7601-merge-pull-config.sh',
+  't7602-merge-octopus-many.sh',
+  't7603-merge-reduce-heads.sh',
+  't7604-merge-custom-message.sh',
+  't7605-merge-resolve.sh',
+  't7606-merge-custom.sh',
+  't7607-merge-state.sh',
+  't7608-merge-messages.sh',
+  't7609-mergetool--lib.sh',
+  't7610-mergetool.sh',
+  't7611-merge-abort.sh',
+  't7612-merge-verify-signatures.sh',
+  't7614-merge-signoff.sh',
+  't7615-diff-algo-with-mergy-operations.sh',
+  't7700-repack.sh',
+  't7701-repack-unpack-unreachable.sh',
+  't7702-repack-cyclic-alternate.sh',
+  't7703-repack-geometric.sh',
+  't7704-repack-cruft.sh',
+  't7800-difftool.sh',
+  't7810-grep.sh',
+  't7811-grep-open.sh',
+  't7812-grep-icase-non-ascii.sh',
+  't7813-grep-icase-iso.sh',
+  't7814-grep-recurse-submodules.sh',
+  't7815-grep-binary.sh',
+  't7816-grep-binary-pattern.sh',
+  't7817-grep-sparse-checkout.sh',
+  't7900-maintenance.sh',
+  't8001-annotate.sh',
+  't8002-blame.sh',
+  't8003-blame-corner-cases.sh',
+  't8004-blame-with-conflicts.sh',
+  't8005-blame-i18n.sh',
+  't8006-blame-textconv.sh',
+  't8007-cat-file-textconv.sh',
+  't8008-blame-formats.sh',
+  't8009-blame-vs-topicbranches.sh',
+  't8010-cat-file-filters.sh',
+  't8011-blame-split-file.sh',
+  't8012-blame-colors.sh',
+  't8013-blame-ignore-revs.sh',
+  't8014-blame-ignore-fuzzy.sh',
+  't9001-send-email.sh',
+  't9002-column.sh',
+  't9003-help-autocorrect.sh',
+  't9100-git-svn-basic.sh',
+  't9101-git-svn-props.sh',
+  't9102-git-svn-deep-rmdir.sh',
+  't9103-git-svn-tracked-directory-removed.sh',
+  't9104-git-svn-follow-parent.sh',
+  't9105-git-svn-commit-diff.sh',
+  't9106-git-svn-commit-diff-clobber.sh',
+  't9107-git-svn-migrate.sh',
+  't9108-git-svn-glob.sh',
+  't9109-git-svn-multi-glob.sh',
+  't9110-git-svn-use-svm-props.sh',
+  't9111-git-svn-use-svnsync-props.sh',
+  't9112-git-svn-md5less-file.sh',
+  't9113-git-svn-dcommit-new-file.sh',
+  't9114-git-svn-dcommit-merge.sh',
+  't9115-git-svn-dcommit-funky-renames.sh',
+  't9116-git-svn-log.sh',
+  't9117-git-svn-init-clone.sh',
+  't9118-git-svn-funky-branch-names.sh',
+  't9119-git-svn-info.sh',
+  't9120-git-svn-clone-with-percent-escapes.sh',
+  't9121-git-svn-fetch-renamed-dir.sh',
+  't9122-git-svn-author.sh',
+  't9123-git-svn-rebuild-with-rewriteroot.sh',
+  't9124-git-svn-dcommit-auto-props.sh',
+  't9125-git-svn-multi-glob-branch-names.sh',
+  't9126-git-svn-follow-deleted-readded-directory.sh',
+  't9127-git-svn-partial-rebuild.sh',
+  't9128-git-svn-cmd-branch.sh',
+  't9129-git-svn-i18n-commitencoding.sh',
+  't9130-git-svn-authors-file.sh',
+  't9131-git-svn-empty-symlink.sh',
+  't9132-git-svn-broken-symlink.sh',
+  't9133-git-svn-nested-git-repo.sh',
+  't9134-git-svn-ignore-paths.sh',
+  't9135-git-svn-moved-branch-empty-file.sh',
+  't9136-git-svn-recreated-branch-empty-file.sh',
+  't9137-git-svn-dcommit-clobber-series.sh',
+  't9138-git-svn-authors-prog.sh',
+  't9139-git-svn-non-utf8-commitencoding.sh',
+  't9140-git-svn-reset.sh',
+  't9141-git-svn-multiple-branches.sh',
+  't9142-git-svn-shallow-clone.sh',
+  't9143-git-svn-gc.sh',
+  't9144-git-svn-old-rev_map.sh',
+  't9145-git-svn-master-branch.sh',
+  't9146-git-svn-empty-dirs.sh',
+  't9147-git-svn-include-paths.sh',
+  't9148-git-svn-propset.sh',
+  't9150-svk-mergetickets.sh',
+  't9151-svn-mergeinfo.sh',
+  't9152-svn-empty-dirs-after-gc.sh',
+  't9153-git-svn-rewrite-uuid.sh',
+  't9154-git-svn-fancy-glob.sh',
+  't9155-git-svn-fetch-deleted-tag.sh',
+  't9156-git-svn-fetch-deleted-tag-2.sh',
+  't9157-git-svn-fetch-merge.sh',
+  't9158-git-svn-mergeinfo.sh',
+  't9159-git-svn-no-parent-mergeinfo.sh',
+  't9160-git-svn-preserve-empty-dirs.sh',
+  't9161-git-svn-mergeinfo-push.sh',
+  't9162-git-svn-dcommit-interactive.sh',
+  't9163-git-svn-reset-clears-caches.sh',
+  't9164-git-svn-dcommit-concurrent.sh',
+  't9165-git-svn-fetch-merge-branch-of-branch.sh',
+  't9166-git-svn-fetch-merge-branch-of-branch2.sh',
+  't9167-git-svn-cmd-branch-subproject.sh',
+  't9168-git-svn-partially-globbed-names.sh',
+  't9169-git-svn-dcommit-crlf.sh',
+  't9200-git-cvsexportcommit.sh',
+  't9210-scalar.sh',
+  't9211-scalar-clone.sh',
+  't9300-fast-import.sh',
+  't9301-fast-import-notes.sh',
+  't9302-fast-import-unpack-limit.sh',
+  't9303-fast-import-compression.sh',
+  't9304-fast-import-marks.sh',
+  't9350-fast-export.sh',
+  't9351-fast-export-anonymize.sh',
+  't9400-git-cvsserver-server.sh',
+  't9401-git-cvsserver-crlf.sh',
+  't9402-git-cvsserver-refs.sh',
+  't9500-gitweb-standalone-no-errors.sh',
+  't9501-gitweb-standalone-http-status.sh',
+  't9502-gitweb-standalone-parse-output.sh',
+  't9600-cvsimport.sh',
+  't9601-cvsimport-vendor-branch.sh',
+  't9602-cvsimport-branches-tags.sh',
+  't9603-cvsimport-patchsets.sh',
+  't9604-cvsimport-timestamps.sh',
+  't9700-perl-git.sh',
+  't9800-git-p4-basic.sh',
+  't9801-git-p4-branch.sh',
+  't9802-git-p4-filetype.sh',
+  't9803-git-p4-shell-metachars.sh',
+  't9804-git-p4-label.sh',
+  't9805-git-p4-skip-submit-edit.sh',
+  't9806-git-p4-options.sh',
+  't9807-git-p4-submit.sh',
+  't9808-git-p4-chdir.sh',
+  't9809-git-p4-client-view.sh',
+  't9810-git-p4-rcs.sh',
+  't9811-git-p4-label-import.sh',
+  't9812-git-p4-wildcards.sh',
+  't9813-git-p4-preserve-users.sh',
+  't9814-git-p4-rename.sh',
+  't9815-git-p4-submit-fail.sh',
+  't9816-git-p4-locked.sh',
+  't9817-git-p4-exclude.sh',
+  't9818-git-p4-block.sh',
+  't9819-git-p4-case-folding.sh',
+  't9820-git-p4-editor-handling.sh',
+  't9821-git-p4-path-variations.sh',
+  't9822-git-p4-path-encoding.sh',
+  't9823-git-p4-mock-lfs.sh',
+  't9824-git-p4-git-lfs.sh',
+  't9825-git-p4-handle-utf16-without-bom.sh',
+  't9826-git-p4-keep-empty-commits.sh',
+  't9827-git-p4-change-filetype.sh',
+  't9828-git-p4-map-user.sh',
+  't9829-git-p4-jobs.sh',
+  't9830-git-p4-symlink-dir.sh',
+  't9831-git-p4-triggers.sh',
+  't9832-unshelve.sh',
+  't9833-errors.sh',
+  't9834-git-p4-file-dir-bug.sh',
+  't9835-git-p4-metadata-encoding-python2.sh',
+  't9836-git-p4-metadata-encoding-python3.sh',
+  't9850-shell.sh',
+  't9901-git-web--browse.sh',
+  't9902-completion.sh',
+  't9903-bash-prompt.sh',
+]
+
+# GIT_BUILD_DIR needs to be Unix-style without drive prefixes as it get added
+# to the PATH variable. And given that drive prefixes contain a colon we'd
+# otherwise end up with a broken PATH if we didn't convert it.
+git_build_dir = meson.project_build_root()
+if cygpath.found()
+  git_build_dir = run_command(cygpath, git_build_dir, check: true).stdout().strip()
+endif
+
+test_environment = script_environment
+test_environment.set('GIT_BUILD_DIR', git_build_dir)
+
+foreach integration_test : integration_tests
+  test(fs.stem(integration_test), shell,
+    args: [ integration_test ],
+    workdir: meson.current_source_dir(),
+    env: test_environment,
+    depends: test_dependencies + bin_wrappers,
+    timeout: 0,
+  )
+endforeach
diff --git a/templates/hooks/meson.build b/templates/hooks/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..5bbb1bd483f24e05f826918fe63391ef72334e11
--- /dev/null
+++ b/templates/hooks/meson.build
@@ -0,0 +1,26 @@
+hooks = [
+  'applypatch-msg.sample',
+  'commit-msg.sample',
+  'fsmonitor-watchman.sample',
+  'post-update.sample',
+  'pre-applypatch.sample',
+  'pre-commit.sample',
+  'pre-merge-commit.sample',
+  'prepare-commit-msg.sample',
+  'pre-push.sample',
+  'pre-rebase.sample',
+  'pre-receive.sample',
+  'push-to-checkout.sample',
+  'sendemail-validate.sample',
+  'update.sample',
+]
+
+foreach hook : hooks
+  configure_file(
+    input: hook,
+    output: hook,
+    configuration: template_config,
+    install: true,
+    install_dir: get_option('datadir') / 'git-core/templates',
+  )
+endforeach
diff --git a/templates/info/meson.build b/templates/info/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..7af5ddd24985fc0a5888961939ae3b2758f2aaa8
--- /dev/null
+++ b/templates/info/meson.build
@@ -0,0 +1,7 @@
+configure_file(
+  input: 'exclude',
+  output: 'exclude',
+  configuration: template_config,
+  install: true,
+  install_dir: get_option('datadir') / 'git-core/templates',
+)
diff --git a/templates/meson.build b/templates/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..1faf9a44ceaa5a6eee673e66c9d5448c08dbc7d5
--- /dev/null
+++ b/templates/meson.build
@@ -0,0 +1,15 @@
+template_config = configuration_data()
+template_config.set('PERL_PATH', perl.found() ? fs.as_posix(perl.full_path()) : '')
+template_config.set('SHELL_PATH', fs.as_posix(shell.full_path()))
+template_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))
+
+configure_file(
+  input: 'description',
+  output: 'description',
+  configuration: template_config,
+  install: true,
+  install_dir: get_option('datadir') / 'git-core/templates',
+)
+
+subdir('hooks')
+subdir('info')
-- 
2.47.0.251.gb31fb630c0.dirty
Christian Couder· Nov 15, 2024, 12:20 UTC · re: Patrick Steinhardt · lore

Re: [PATCH RFC v7 21/22] Introduce support for the Meson build system

On Fri, Nov 15, 2024 at 8:21 AM Patrick Steinhardt <ps@pks.im> wrote:
Show 25 quoted lines
> +# Basic usage
> +# ===========
> +#
> +# In the most trivial case, you can configure, build and install Git like this:
> +#
> +#  1. Set up the build directory. This only needs to happen once per build
> +#     directory you want to have. You can also configure multiple different
> +#     build directories with different configurations.
> +#
> +#      $ meson setup build/
> +#
> +#     The build directory gets ignored by Git automatically as Meson will write
> +#     a ".gitignore" file into it. From hereon, we will assume that you execute
> +#     commands inside this build directory.
> +#
> +# 2. Compile Git. You can either use Meson, Ninja or Samurai to do this, so all
> +#    of the following invocations are equivalent:
> +#
> +#      $ meson compile
> +#      $ ninja
> +#      $ samu
> +#
> +#   The different invocations should ultimately not make much of a difference.
> +#   Using Meson also works with other generators though, like when the build
> +#   directory has been set up for use with Microsoft Visual Studio.

I think it might be helpful to say if the above commands are using multiple processes in parallel (like `make -j 10` for example) to build or not. If they are using multiple processes, it might be useful to shortly say how the number of processes used is computed. If they are not using multiple processes, it might be interesting to say if an option exists for this or not, and if not, why.

If the above commands use ccache or can use it, or similar tools, it could be interesting to say so too.

Show 23 quoted lines
> +#
> +# 3. Execute tests. Again, you can either use Meson, Ninja or Samurai to do this:
> +#
> +#      $ meson test
> +#      $ ninja test
> +#      $ samu test
> +#
> +#   It is recommended to use Meson in this case though as it also provides you
> +#   additional features that the other build systems don't have available.
> +#   You can e.g. pass additional arguments to the test executables or run
> +#   individual tests:
> +#
> +#      # Execute the t0000-basic integration test and t-reftable-stack unit test.
> +#      $ meson test t0000-basic t-reftable-stack
> +#
> +#      # Execute all reftable unit tests.
> +#      $ meson test t-reftable-*
> +#
> +#      # Execute all tests and stop with the first failure.
> +#      $ meson test --maxfail 1
> +#
> +#      # Execute single test interactively such that features like `debug ()` work.
> +#      $ meson test -i --test-args='-ix' t1400-update-ref

How about executing tests in parallel? I am asking because it's an easy way to speed up test execution.

Show 14 quoted lines
> +# 4. Install the Git distribution. Again, this can be done via Meson, Ninja or
> +#    Samurai:
> +#
> +#      $ meson install
> +#      $ ninja install
> +#      $ samu install
> +#
> +#    The prefix into which Git shall be installed is defined when setting up
> +#    the build directory. More on that in the "Configuration" section.
> +#
> +# Meson supports multiple backends. The default backend generates Ninja build
> +# instructions, but it also supports the generation of Microsoft Visual
> +# Studio solutions as well as Xcode projects. IDEs like Eclipse and Visual
> +# Studio Code provide plugins to import Meson files directly.

So I guess it's not necessary to configure Meson to make it use a backend that generates VS Code or Xcode projects.

Show 22 quoted lines
> +# Configuration
> +# =============
> +#
> +# The exact configuration of Git is determined when setting up the build
> +# directory via `meson setup`. Unless told otherwise, Meson will automatically
> +# detect the availability of various bits and pieces. There are two different
> +# kinds of options that can be used to further tweak the build:
> +#
> +#   - Built-in options provided by Meson.
> +#
> +#   - Options defined by the project in the "meson_options.txt" file.
> +#
> +# Both kinds of options can be inspected by running `meson configure` in the
> +# build directory, which will give you a list of the current value for all
> +# options.
> +#
> +# Options can be configured either when setting up the build directory or can
> +# be changed in preexisting build directories:
> +#
> +#      # Set up a new build directory with optimized settings that will be
> +#      # installed into an alternative prefix.
> +#      $ meson setup --buildtype release --optimization 3 --strip --prefix=/home/$USER build

"build" is the name that was already given to the build directory above in this doc, so it might be better to use something else, like maybe "build-optimized/".

> +#      # Set up a new build directory with a higher warning level. Level 2 is
> +#      # mostly equivalent to setting DEVELOPER=1, level 3 and "everything"
> +#      # will enable even more warnings.
> +#      $ meson setup -Dwarning_level=2

There is no name for the new build directory in the above command, maybe something like "build-warning/" should be added.

> +#      # Set up a new build directory with 'address' and 'undefined' sanitizers
> +#      # using Clang.
> +#      $ CC=clang meson setup -Db_sanitize=address,undefined build
Maybe "build-address/" instead of "build".
> +#      # Disable tests in a preexisting build directory.
> +#      $ meson configure -Dtests=false
What happens if we run `meson test` then?
Show 12 quoted lines
> +#      # Disable features based on Python
> +#      $ meson configure -Dpython=disabled
> +#
> +# Options have a type like booleans, choices, strings or features. Features are
> +# somewhat special as they can have one of three values: enabled, disabled or
> +# auto. While the first two values are self-explanatory, "auto" will enable or
> +# disable the feature based on the availability of prerequisites to support it.
> +# Python-based features for example will be enabled automatically when a Python
> +# interpreter could be found. The default value of such features can be changed
> +# globally via `meson setup --auto-features={enabled,disabled,auto}`, which
> +# will set the value of all features with a value of "auto" to the provided one
> +# by default.

"globally" here means for all the builds directories of the current system user, or for all the build directories inside the directory where `meson setup --auto-features={enabled,disabled,auto}` was run, or just for all the features in the current build directory if this is run inside a build directory?

Show 16 quoted lines
> +# It is also possible to store a set of configuration options in machine files.
> +# This can be useful in case you regularly want to reuse the same set of options:
> +#
> +#   [binaries]
> +#   c = ['clang']
> +#   ar = ['ar']
> +#
> +#   [project options]
> +#   gettext = 'disabled'
> +#   default_editor = 'vim'
> +#
> +#   [built-in options]
> +#   b_lto = true
> +#   b_sanitize = 'address,undefined'
> +#
> +# These machine files can be passed to Meson via `meson setup --native-file`.

I think it might be helpful to say that "machine files" is the name used by meson for basically "config files".

Also it's not very clear which build directories will be impacted when `meson setup --native-file my-machine-file` is run.

> +# Subproject wrappers
> +# ===================
> +#
> +# Subproject wrappers are a feature provided by Meson that allow the automatic
s/allow/allows/
Show 8 quoted lines
> +# fallback to a "wrapped" dependency in case the dependency is not provided by
> +# the system. For example if the system is lacking curl, then Meson will use
> +# "subprojects/curl.wrap" to set up curl as a subproject and compile and link
> +# the dependency into Git itself. This is especially helpful on systems like
> +# Windows, where you typically don't have such dependencies installed.
> +#
> +# The use of subproject wrappers can be disabled by executing `meson setup
> +# --wrap-mode nofallback`.
It's not very clear which build directories will be impacted by this command.
Thanks.
Patrick Steinhardt· Nov 18, 2024, 09:29 UTC · re: Christian Couder · lore

Re: [PATCH RFC v7 21/22] Introduce support for the Meson build system

On Fri, Nov 15, 2024 at 01:20:55PM +0100, Christian Couder wrote:
Show 34 quoted lines
> On Fri, Nov 15, 2024 at 8:21 AM Patrick Steinhardt <ps@pks.im> wrote:
> 
> > +# Basic usage
> > +# ===========
> > +#
> > +# In the most trivial case, you can configure, build and install Git like this:
> > +#
> > +#  1. Set up the build directory. This only needs to happen once per build
> > +#     directory you want to have. You can also configure multiple different
> > +#     build directories with different configurations.
> > +#
> > +#      $ meson setup build/
> > +#
> > +#     The build directory gets ignored by Git automatically as Meson will write
> > +#     a ".gitignore" file into it. From hereon, we will assume that you execute
> > +#     commands inside this build directory.
> > +#
> > +# 2. Compile Git. You can either use Meson, Ninja or Samurai to do this, so all
> > +#    of the following invocations are equivalent:
> > +#
> > +#      $ meson compile
> > +#      $ ninja
> > +#      $ samu
> > +#
> > +#   The different invocations should ultimately not make much of a difference.
> > +#   Using Meson also works with other generators though, like when the build
> > +#   directory has been set up for use with Microsoft Visual Studio.
> 
> I think it might be helpful to say if the above commands are using
> multiple processes in parallel (like `make -j 10` for example) to
> build or not. If they are using multiple processes, it might be useful
> to shortly say how the number of processes used is computed. If they
> are not using multiple processes, it might be interesting to say if an
> option exists for this or not, and if not, why.

Yup, will add a note. Ninja and Samurai scale with the number of processor cores by default. The number of jobs can be changed with the `-jN` flag.

> If the above commands use ccache or can use it, or similar tools, it
> could be interesting to say so too.
Added a note.
Show 26 quoted lines
> > +#
> > +# 3. Execute tests. Again, you can either use Meson, Ninja or Samurai to do this:
> > +#
> > +#      $ meson test
> > +#      $ ninja test
> > +#      $ samu test
> > +#
> > +#   It is recommended to use Meson in this case though as it also provides you
> > +#   additional features that the other build systems don't have available.
> > +#   You can e.g. pass additional arguments to the test executables or run
> > +#   individual tests:
> > +#
> > +#      # Execute the t0000-basic integration test and t-reftable-stack unit test.
> > +#      $ meson test t0000-basic t-reftable-stack
> > +#
> > +#      # Execute all reftable unit tests.
> > +#      $ meson test t-reftable-*
> > +#
> > +#      # Execute all tests and stop with the first failure.
> > +#      $ meson test --maxfail 1
> > +#
> > +#      # Execute single test interactively such that features like `debug ()` work.
> > +#      $ meson test -i --test-args='-ix' t1400-update-ref
> 
> How about executing tests in parallel? I am asking because it's an
> easy way to speed up test execution.

Tests are executed in parallel by default. Same as above, you can change this with `-jN`.

Show 17 quoted lines
> > +# 4. Install the Git distribution. Again, this can be done via Meson, Ninja or
> > +#    Samurai:
> > +#
> > +#      $ meson install
> > +#      $ ninja install
> > +#      $ samu install
> > +#
> > +#    The prefix into which Git shall be installed is defined when setting up
> > +#    the build directory. More on that in the "Configuration" section.
> > +#
> > +# Meson supports multiple backends. The default backend generates Ninja build
> > +# instructions, but it also supports the generation of Microsoft Visual
> > +# Studio solutions as well as Xcode projects. IDEs like Eclipse and Visual
> > +# Studio Code provide plugins to import Meson files directly.
> 
> So I guess it's not necessary to configure Meson to make it use a
> backend that generates VS Code or Xcode projects.
I've added a note on how to set the backend.
Show 26 quoted lines
> > +# Configuration
> > +# =============
> > +#
> > +# The exact configuration of Git is determined when setting up the build
> > +# directory via `meson setup`. Unless told otherwise, Meson will automatically
> > +# detect the availability of various bits and pieces. There are two different
> > +# kinds of options that can be used to further tweak the build:
> > +#
> > +#   - Built-in options provided by Meson.
> > +#
> > +#   - Options defined by the project in the "meson_options.txt" file.
> > +#
> > +# Both kinds of options can be inspected by running `meson configure` in the
> > +# build directory, which will give you a list of the current value for all
> > +# options.
> > +#
> > +# Options can be configured either when setting up the build directory or can
> > +# be changed in preexisting build directories:
> > +#
> > +#      # Set up a new build directory with optimized settings that will be
> > +#      # installed into an alternative prefix.
> > +#      $ meson setup --buildtype release --optimization 3 --strip --prefix=/home/$USER build
> 
> "build" is the name that was already given to the build directory
> above in this doc, so it might be better to use something else, like
> maybe "build-optimized/".

It's only an example anyway, so I don't think it matters much whether the directory names repeat. Same with the following commands.

Show 18 quoted lines
> > +#      # Set up a new build directory with a higher warning level. Level 2 is
> > +#      # mostly equivalent to setting DEVELOPER=1, level 3 and "everything"
> > +#      # will enable even more warnings.
> > +#      $ meson setup -Dwarning_level=2
> 
> There is no name for the new build directory in the above command,
> maybe something like "build-warning/" should be added.
> 
> > +#      # Set up a new build directory with 'address' and 'undefined' sanitizers
> > +#      # using Clang.
> > +#      $ CC=clang meson setup -Db_sanitize=address,undefined build
> 
> Maybe "build-address/" instead of "build".
> 
> > +#      # Disable tests in a preexisting build directory.
> > +#      $ meson configure -Dtests=false
> 
> What happens if we run `meson test` then?
Meson simply prints "No tests defined."
Show 18 quoted lines
> > +#      # Disable features based on Python
> > +#      $ meson configure -Dpython=disabled
> > +#
> > +# Options have a type like booleans, choices, strings or features. Features are
> > +# somewhat special as they can have one of three values: enabled, disabled or
> > +# auto. While the first two values are self-explanatory, "auto" will enable or
> > +# disable the feature based on the availability of prerequisites to support it.
> > +# Python-based features for example will be enabled automatically when a Python
> > +# interpreter could be found. The default value of such features can be changed
> > +# globally via `meson setup --auto-features={enabled,disabled,auto}`, which
> > +# will set the value of all features with a value of "auto" to the provided one
> > +# by default.
> 
> "globally" here means for all the builds directories of the current
> system user, or for all the build directories inside the directory
> where `meson setup --auto-features={enabled,disabled,auto}` was run,
> or just for all the features in the current build directory if this is
> run inside a build directory?
The last one. I'll just drop this word.
Show 22 quoted lines
> > +# It is also possible to store a set of configuration options in machine files.
> > +# This can be useful in case you regularly want to reuse the same set of options:
> > +#
> > +#   [binaries]
> > +#   c = ['clang']
> > +#   ar = ['ar']
> > +#
> > +#   [project options]
> > +#   gettext = 'disabled'
> > +#   default_editor = 'vim'
> > +#
> > +#   [built-in options]
> > +#   b_lto = true
> > +#   b_sanitize = 'address,undefined'
> > +#
> > +# These machine files can be passed to Meson via `meson setup --native-file`.
> 
> I think it might be helpful to say that "machine files" is the name
> used by meson for basically "config files".
> 
> Also it's not very clear which build directories will be impacted when
> `meson setup --native-file my-machine-file` is run.

As `meson setup` sets up a new build directory it is that particular directory. I've reworded this a bit.

Show 17 quoted lines
> > +# Subproject wrappers
> > +# ===================
> > +#
> > +# Subproject wrappers are a feature provided by Meson that allow the automatic
> 
> s/allow/allows/
> 
> > +# fallback to a "wrapped" dependency in case the dependency is not provided by
> > +# the system. For example if the system is lacking curl, then Meson will use
> > +# "subprojects/curl.wrap" to set up curl as a subproject and compile and link
> > +# the dependency into Git itself. This is especially helpful on systems like
> > +# Windows, where you typically don't have such dependencies installed.
> > +#
> > +# The use of subproject wrappers can be disabled by executing `meson setup
> > +# --wrap-mode nofallback`.
> 
> It's not very clear which build directories will be impacted by this command.
Reformulated slightly.
Thanks for your feedback!
Patrick
Patrick Steinhardt· Nov 15, 2024, 07:21 UTC · re: Patrick Steinhardt · lore

[PATCH RFC v7 22/22] meson: fix conflicts with in-flight topics

As support for Meson is still in-flight we have to accommodate for conflicts with topics in "seen". The following conflicts are being addressed in this commit:

  - ej/cat-file-remote-object-info adds t1017 and "fetch-object-info.c".
  - cc/promisor-remote-capability adds t5710.
  - ds/path-walk adds t6601 as well as "path-walk.c" and
    "test-path-walk.c".
  - ps/reftable-detach adds "reftable/system.c".
  - js/libgit-rust adds "common-exit.c" and "common-init.c".
  - ps/clar-build-improvement adapts the awk(1) script that generates
    "clar.suites" to instead be a shell script.
  - ds/full-name-hash adds "t/helper/test-name-hash.c".
  - cw/worktree-extension deletes t2408.

This is somewhat painful in the current state where Meson is not yet part of the main tree, but we'll have to live with that for the time being.

I've split this commit out into a separate fixup-style commit such that it is possible to test this topic both with and without "seen" merged into it. You can simply revert this commit to test without "seen".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 meson.build          | 5 +++++
 t/helper/meson.build | 2 ++
 t/meson.build        | 9 +++++----
 3 files changed, 12 insertions(+), 4 deletions(-)
Show changes to 3 files +12 −4

meson.build, t/helper/meson.build, t/meson.build

diff --git a/meson.build b/meson.build
index 17974eafdd86d9c1b6696868f1497b71caa165a3..f5f6bd183f187540a9a0aa1e02d276d819f54d7a 100644
--- a/meson.build
+++ b/meson.build
@@ -217,6 +217,8 @@ libgit_sources = [
   'commit-graph.c',
   'commit-reach.c',
   'commit.c',
+  'common-exit.c',
+  'common-init.c',
   'compat/nonblock.c',
   'compat/obstack.c',
   'compat/terminal.c',
@@ -255,6 +257,7 @@ libgit_sources = [
   'ewah/ewah_rlw.c',
   'exec-cmd.c',
   'fetch-negotiator.c',
+  'fetch-object-info.c',
   'fetch-pack.c',
   'fmt-merge-msg.c',
   'fsck.c',
@@ -329,6 +332,7 @@ libgit_sources = [
   'parse-options.c',
   'patch-delta.c',
   'patch-ids.c',
+  'path-walk.c',
   'path.c',
   'pathspec.c',
   'pkt-line.c',
@@ -369,6 +373,7 @@ libgit_sources = [
   'reftable/reader.c',
   'reftable/record.c',
   'reftable/stack.c',
+  'reftable/system.c',
   'reftable/tree.c',
   'reftable/writer.c',
   'remote.c',
diff --git a/t/helper/meson.build b/t/helper/meson.build
index 5e83884246edc7841738de5085f3255aa1fa3fbe..1d6154ce9756db17bc9f69bc3cd71a32b93857c5 100644
--- a/t/helper/meson.build
+++ b/t/helper/meson.build
@@ -34,12 +34,14 @@ test_tool_sources = [
   'test-match-trees.c',
   'test-mergesort.c',
   'test-mktemp.c',
+  'test-name-hash.c',
   'test-online-cpus.c',
   'test-pack-mtimes.c',
   'test-parse-options.c',
   'test-parse-pathspec-file.c',
   'test-partial-clone.c',
   'test-path-utils.c',
+  'test-path-walk.c',
   'test-pcre2-config.c',
   'test-pkt-line.c',
   'test-proc-receive.c',
diff --git a/t/meson.build b/t/meson.build
index 9ec0b7fa34a033d508448dbd56ab2322a5106fd0..8f0ba136eeb0558f866d7fd3f0559d94139d4777 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -19,9 +19,8 @@ clar_sources += clar_decls_h
 clar_sources += custom_target(
   input: clar_decls_h,
   output: 'clar.suite',
-  feed: true,
-  capture: true,
-  command : [awk, '-f', meson.current_source_dir() + '/unit-tests/clar-generate.awk'],
+  command : [shell, meson.current_source_dir() + '/unit-tests/generate-clar-suites.sh', '@INPUT@', '@OUTPUT@'],
+  env: script_environment,
 )
 
 clar_unit_tests = executable('unit-tests',
@@ -168,6 +167,7 @@ integration_tests = [
   't1014-read-tree-confusing.sh',
   't1015-read-index-unmerged.sh',
   't1016-compatObjectFormat.sh',
+  't1017-cat-file-remote-object-info.sh',
   't1020-subdirectory.sh',
   't1021-rerere-in-workdir.sh',
   't1022-read-tree-partial-clone.sh',
@@ -297,7 +297,6 @@ integration_tests = [
   't2405-worktree-submodule.sh',
   't2406-worktree-repair.sh',
   't2407-worktree-heads.sh',
-  't2408-worktree-relative.sh',
   't2500-untracked-overwriting.sh',
   't2501-cwd-empty.sh',
   't3000-ls-files-others.sh',
@@ -719,6 +718,7 @@ integration_tests = [
   't5703-upload-pack-ref-in-want.sh',
   't5704-protocol-violations.sh',
   't5705-session-id-in-capabilities.sh',
+  't5710-promisor-remote-capability.sh',
   't5730-protocol-v2-bundle-uri-file.sh',
   't5731-protocol-v2-bundle-uri-git.sh',
   't5732-protocol-v2-bundle-uri-http.sh',
@@ -821,6 +821,7 @@ integration_tests = [
   't6500-gc.sh',
   't6501-freshen-objects.sh',
   't6600-test-reach.sh',
+  't6601-path-walk.sh',
   't6700-tree-depth.sh',
   't7001-mv.sh',
   't7002-mv-sparse-checkout.sh',
-- 
2.47.0.251.gb31fb630c0.dirty
Patrick Steinhardt· Nov 15, 2024, 11:17 UTC · re: Patrick Steinhardt · lore

Re: [PATCH RFC v7 00/22] Modernize the build system

On Fri, Nov 15, 2024 at 08:21:12AM +0100, Patrick Steinhardt wrote:
Show 5 quoted lines
> Hi,
> 
> this patch series modernizes our build infrasturcture. It refactors
> various parts of it to make it possible to perform out-of-tree builds in
> theory.

I noticed that building documentation is broken in this version. I've corrected that up locally, so this will be fixed in the next reroll.

Patrick
Ramsay Jones· Nov 16, 2024, 01:23 UTC · re: Patrick Steinhardt · lore

Re: [-SPAM-] [PATCH RFC v7 00/22] Modernize the build system

On 15/11/2024 07:21, Patrick Steinhardt wrote: [snip]

Show 23 quoted lines
> Changes in v7:
> 
>   - Fix overriding TEST_OUTPUT_DIRECTORY via the environment.
> 
>   - Rework generation of version information. GIT-VERSION-GEN now knows
>     to replace @PLACEHOLDERS@ in other files, and this gets used across
>     Makefiles, CMake and Meson now. This makes us properly rebuild when
>     the version info changes and fixes the version numbers in "git.rc".
> 
>   - The CMake build instructions are now doing a proper out-of-tree
>     build.
> 
>   - We now generate and install gitweb.js, which gets built via a new
>     "generate-gitweb-js.sh".
> 
>   - Some improvements to the Meson documentation.
> 
>   - Wire up DEVELOPER=1 style warnings via `--warnlevel 2` and above.
> 
>   - Wire up sanitizers properly.
> 
>   - Provide a new "regex" option to disable the platform-provided
>     version of regcomp() et al.
Hmm, OK, but what about all the other 'configurations'?
Show 13 quoted lines
> 
>   - Stop installing git-http-backend into bindir.
> 
>   - Install Perl scripts into libexecdir.
> 
>   - Install git-cvsserver into bindir and libexecdir.
> 
>   - Install template directory into datadir.
> 
> As before, the tip of this patch series catches up with what is
> happening in "seen". If you want to test it without topics in "seen" you
> have to revert it.
> 

Just a quick note to say that I tested this on Linux and Cygwin (just build) and noticed a few things:

  - 'make clean' does not delete the 'version-def.h' file.
  - the RUNTIME_PREFIX set in the GIT-BUILD-OPTIONS file on the master
    branch is not the same as on this 'meson-v7' branch (patches 1-21
    based on current master branch):
    $ diff GBO-master GIT-BUILD-OPTIONS
    3a4,5
    > PERL_LOCALEDIR='/home/ramsay/share/locale'
    > NO_PERL_CPAN_FALLBACKS=''
    19a22,28
    > FSMONITOR_DAEMON_BACKEND=''
    > FSMONITOR_OS_SETTINGS=''
    > TEST_OUTPUT_DIRECTORY=''
    > GIT_TEST_OPTS=''
    > GIT_TEST_CMP=''
    > GIT_TEST_CMP_USE_COPIED_CONTEXT=''
    > GIT_TEST_UTF8_LOCALE=''
    20a30,36
    > GIT_PERF_REPEAT_COUNT=''
    > GIT_PERF_REPO=''
    > GIT_PERF_LARGE_REPO=''
    > GIT_PERF_MAKE_OPTS=''
    > GIT_PERF_MAKE_COMMAND=''
    > GIT_INTEROP_MAKE_OPTS=''
    > GIT_TEST_INDEX_VERSION=''
    22c38,47
    < RUNTIME_PREFIX='false'
    ---
    > GIT_TEST_TEXTDOMAINDIR='/home/ramsay/git/po/build/locale'
    > GIT_TEST_POPATH='/home/ramsay/git/po'
    > GIT_TEST_TEMPLATE_DIR='/home/ramsay/git/templates/blt'
    > GIT_TEST_GITPERLLIB='/home/ramsay/git/perl/build/lib'
    > GIT_TEST_MERGE_TOOLS_DIR='/home/ramsay/git/mergetools'
    > RUNTIME_PREFIX=''
    > GITWEBDIR='/home/ramsay/share/gitweb'
    > USE_GETTEXT_SCHEME=''
    > LOCALEDIR='/home/ramsay/share/locale'
    > BROKEN_PATH_FIX='/^# @BROKEN_PATH_FIX@$/d'
    $ 

Note that master has RUNTIME_PREFIX='false' rather than being un-set. (Indeed, the Makefile on the master branch normalizes the value of RUNTIME_PREFIX to 'true' or 'false').

    - I used the new 'regex' variable, on cygwin, like so:
    $ meson setup .. -Dprefix=$HOME -Dregex=disabled
    after first trying '-Dregex' and then '-Dregex=1', because why not? ;)
    This correctly set NO_REGEX=1 in the build.
    - As a result of the above, the grep-binary test now behaves the same
      way as on Linux (ie that test now 'fails' instead of unexpectedly
      passing). Note:
    $ meson test 't7815-*'
    ninja: Entering directory `/home/ramsay/git/build'
    [2/9] Generating GIT-VERSION-FILE with a custom command
    1/1 t7815-grep-binary        OK              7.55s
    Ok:                 1   
    Expected Fail:      0   
    Fail:               0   
    Unexpected Pass:    0   
    Skipped:            0   
    Timeout:            0   
    Full log written to /home/ramsay/git/build/meson-logs/testlog.txt
    $ vim meson-logs/testlog.txt # test 12 again '# TODO known breakage'
    $ 
    - The format of the GIT-BUILD-OPTIONS file generated by the Makefile
      and meson are formatted differently, so you have to massage the
      top-level file to be able to diff them:
    $ sed -e 's/\x27\x27//' GIT-BUILD-OPTIONS |
    > sed -e 's/\x27/"/g' >GBO
    $ diff GBO build/GIT-BUILD-OPTIONS 
    1,2c1,2
    < SHELL_PATH="/bin/sh"
    < TEST_SHELL_PATH="/bin/sh"
    ---
    > SHELL_PATH="/usr/bin/sh"
    > TEST_SHELL_PATH="/usr/bin/sh"
    6c6
    < DIFF="diff"
    ---
    > DIFF="/usr/bin/diff"
    8c8
    < TAR="tar"
    ---
    > TAR="/usr/bin/tar"
    12c12
    < USE_LIBPCRE2=
    ---
    > USE_LIBPCRE2=1
    21,24c21,24
    < X=
    < FSMONITOR_DAEMON_BACKEND=
    < FSMONITOR_OS_SETTINGS=
    < TEST_OUTPUT_DIRECTORY=
    ---
    > X=""
    > FSMONITOR_DAEMON_BACKEND=""
    > FSMONITOR_OS_SETTINGS=""
    > TEST_OUTPUT_DIRECTORY="/home/ramsay/git/build/test-output"
    26c26
    < GIT_TEST_CMP=
    ---
    > GIT_TEST_CMP="/usr/bin/diff -u"
    37,38c37,38
    < GIT_TEST_PERL_FATAL_WARNINGS="YesPlease"
    < GIT_TEST_TEXTDOMAINDIR="/home/ramsay/git/po/build/locale"
    ---
    > GIT_TEST_PERL_FATAL_WARNINGS=
    > GIT_TEST_TEXTDOMAINDIR="/home/ramsay/git/build/po"
    40,41c40,41
    < GIT_TEST_TEMPLATE_DIR="/home/ramsay/git/templates/blt"
    < GIT_TEST_GITPERLLIB="/home/ramsay/git/perl/build/lib"
    ---
    > GIT_TEST_TEMPLATE_DIR="/home/ramsay/git/build/templates"
    > GIT_TEST_GITPERLLIB="/home/ramsay/git/build/perl"
    44c44
    < GITWEBDIR="/home/ramsay/share/gitweb"
    ---
    > GITWEBDIR=/home/ramsay/share/gitweb
    47c47
    < BROKEN_PATH_FIX="/^# @BROKEN_PATH_FIX@$/d"
    ---
    > BROKEN_PATH_FIX=
    $ 

Note that the Makefile build on Linux does not, by default, build with libpcre, even if the library is installed on the system (as it is for me). Also, note the values of BROKEN_PATH_FIX.

[This was on Linux; cygwin is similar but not identical]

ATB, Ramsay Jones

    
Patrick Steinhardt· Nov 18, 2024, 09:30 UTC · re: Ramsay Jones · lore

Re: [-SPAM-] [PATCH RFC v7 00/22] Modernize the build system

On Sat, Nov 16, 2024 at 01:23:19AM +0000, Ramsay Jones wrote:
Show 27 quoted lines
> On 15/11/2024 07:21, Patrick Steinhardt wrote:
> [snip]
> > Changes in v7:
> > 
> >   - Fix overriding TEST_OUTPUT_DIRECTORY via the environment.
> > 
> >   - Rework generation of version information. GIT-VERSION-GEN now knows
> >     to replace @PLACEHOLDERS@ in other files, and this gets used across
> >     Makefiles, CMake and Meson now. This makes us properly rebuild when
> >     the version info changes and fixes the version numbers in "git.rc".
> > 
> >   - The CMake build instructions are now doing a proper out-of-tree
> >     build.
> > 
> >   - We now generate and install gitweb.js, which gets built via a new
> >     "generate-gitweb-js.sh".
> > 
> >   - Some improvements to the Meson documentation.
> > 
> >   - Wire up DEVELOPER=1 style warnings via `--warnlevel 2` and above.
> > 
> >   - Wire up sanitizers properly.
> > 
> >   - Provide a new "regex" option to disable the platform-provided
> >     version of regcomp() et al.
> 
> Hmm, OK, but what about all the other 'configurations'?

I don't think it makes sense to introduce a separate option for every single bit of the build process that can be changed. One of the biggest differences compared to our Makefile is that Meson should be able to automatically figure out the build environment and set up the build accordingly, without any user interaction. So in the best case, whenever we learn that a system does not work out of the box we'd not introduce an option for this, but instead adapt the autoconfiguration logic to detect that.

Now there will be exceptions where it does make sense to make something a build option, due to whatever reason. But I'm in the camp of adding them as-needed and not proactively so that we don't end up with hundreds of options.

Show 17 quoted lines
> >   - Stop installing git-http-backend into bindir.
> > 
> >   - Install Perl scripts into libexecdir.
> > 
> >   - Install git-cvsserver into bindir and libexecdir.
> > 
> >   - Install template directory into datadir.
> > 
> > As before, the tip of this patch series catches up with what is
> > happening in "seen". If you want to test it without topics in "seen" you
> > have to revert it.
> > 
> 
> Just a quick note to say that I tested this on Linux and Cygwin (just build)
> and noticed a few things:
> 
>   - 'make clean' does not delete the 'version-def.h' file.
This works now with the latest option.
>   - the RUNTIME_PREFIX set in the GIT-BUILD-OPTIONS file on the master
>     branch is not the same as on this 'meson-v7' branch (patches 1-21
>     based on current master branch):
> 
[snip]
> 
> Note that master has RUNTIME_PREFIX='false' rather than being un-set.
> (Indeed, the Makefile on the master branch normalizes the value of
> RUNTIME_PREFIX to 'true' or 'false').
Ah, good catch, corrected.
>     - The format of the GIT-BUILD-OPTIONS file generated by the Makefile
>       and meson are formatted differently, so you have to massage the
>       top-level file to be able to diff them:
> 
[snip]
> 
> Note that the Makefile build on Linux does not, by default, build with
> libpcre, even if the library is installed on the system (as it is for me).

I think building with libpcre by default does make sense though. I guess that we don't with our Makefile because it's not doing autodetection of available features in the first place, so it was safer to not compile with the library by default.

> Also, note the values of BROKEN_PATH_FIX.
I've wired up the SANE_TOOL_PATH option properly now.
Thanks for your feedback!
Patrick
Junio C Hamano· Nov 18, 2024, 12:48 UTC · re: Patrick Steinhardt · lore

Re: [-SPAM-] [PATCH RFC v7 00/22] Modernize the build system

Patrick Steinhardt <ps@pks.im> writes:
> Now there will be exceptions where it does make sense to make something
> a build option, due to whatever reason. But I'm in the camp of adding
> them as-needed and not proactively so that we don't end up with hundreds
> of options.

Surely, your system may have openssl installed, but for non-technical reasons you may not want your Git to link with it, which is impossible to auto-detect, of course.

To be quite honest, while I am very much sympathetic to the above, in practice, I am afraid that these knobs we _already_ have in our Makefile are exceptions that was added due to whatever reason "as needed". Dropping any of them would mean that you are declaring this: by default, all the past exceptions we made are nullified and you have to resubmit for requalification if you want to keep your favourite one.

That's not a very nice way to run the project, though.
Thanks.
Patrick Steinhardt· Nov 18, 2024, 13:05 UTC · re: Junio C Hamano · lore

Re: [-SPAM-] [PATCH RFC v7 00/22] Modernize the build system

On Mon, Nov 18, 2024 at 09:48:06PM +0900, Junio C Hamano wrote:
Show 20 quoted lines
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > Now there will be exceptions where it does make sense to make something
> > a build option, due to whatever reason. But I'm in the camp of adding
> > them as-needed and not proactively so that we don't end up with hundreds
> > of options.
> 
> Surely, your system may have openssl installed, but for
> non-technical reasons you may not want your Git to link with it,
> which is impossible to auto-detect, of course.
> 
> To be quite honest, while I am very much sympathetic to the above,
> in practice, I am afraid that these knobs we _already_ have in our
> Makefile are exceptions that was added due to whatever reason "as
> needed".  Dropping any of them would mean that you are declaring
> this: by default, all the past exceptions we made are nullified and
> you have to resubmit for requalification if you want to keep your
> favourite one.
> 
> That's not a very nice way to run the project, though.

There is a difference though: you are talking about actual features wired into Git, whereas I'm talking about other things like "Does the system have `uintmax_t`?".

The former is a strong must to be toggled by the user, and it is already wired up like this: you can choose to compile without OpenSSL and any other optional library with Meson. All toggles we have in this context in our Makefile must be supported by Meson. While auto-detection is nice to have, it is mandatory that the detection can be disabled.

But for the second group I'm sceptical whether these really should be configurable by the developer. Why would you care whether you use `uintmax_t` provided by the system or by Git as a compat macro? I don't see a strong reason to let the developer override this in a build system that knows to auto-detect the availability of such features.

Now as said, there may be cases where it indeed is useful, but in general I don't think it is.

Patrick
Junio C Hamano· Nov 18, 2024, 23:39 UTC · re: Patrick Steinhardt · lore

Re: [-SPAM-] [PATCH RFC v7 00/22] Modernize the build system

Patrick Steinhardt <ps@pks.im> writes:
Show 8 quoted lines
> But for the second group I'm sceptical whether these really should be
> configurable by the developer. Why would you care whether you use
> `uintmax_t` provided by the system or by Git as a compat macro? I don't
> see a strong reason to let the developer override this in a build system
> that knows to auto-detect the availability of such features.
>
> Now as said, there may be cases where it indeed is useful, but in
> general I don't think it is.

I think we are on the same page, then. When you said "detect", my primarily thought was "uintmax_t and the presense of OpenSSL are both auto-detectable, but the latter is a choice---forcing folks to fight again to support the choice is unnice", while auto-detection of the former in my mind was a given (perhaps think of "make + autoconf" in an ideal world as a baseline for comparison).

There may be cases where it may be handy to pretend that types and library functions your system offers do _not_ exist, for various purposes like test-building or performance tuning, but these purposes are of lessor importance than exclusion by policy, I would say.

← back to recent threads