meson: distinguish build and target host binaries

Almost all of the tools we discover during the build process need to be
native programs. There are only a handful of exceptions, which typically
are programs whose paths we need to embed into the resulting executable
so that they can be found on the target system when Git executes. While
this distinction typically doesn't matter, it does start to matter when
considering cross-compilation where the build and target machines are
different.

Meson supports cross-compilation via so-called machine files. These
machine files allow the user to override parameters for the build
machine, but also for the target machine when cross-compiling. Part of
the machine file is a section that allows the user to override the
location where binaries are to be found in the target system. The
following machine file would for example override the path of the POSIX
shell:

    [binaries]
    sh = '/usr/xpg4/bin/sh'

It can be handed over to Meson via `meson setup --cross-file`.

We do not handle this correctly right now though because we don't know
to distinguish binaries for the build and target hosts at all. Address
this by explicitly passing the `native:` parameter to `find_program()`:

  - When set to `true`, we get binaries discovered on the build host.

  - When set to `false`, we get either the path specified in the
    machine file. Or, if no machine file exists or it doesn't specify
    the binary path, then we fall back to the binary discovered on the
    build host.

As mentioned, only a handful of binaries are not native: only the system
shell, Python and Perl need to be treated specially here.

Reported-by: Peter Seiderer <ps.report@gmx.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Patrick Steinhardt 2025-03-31 10:33:10 +02:00 committed by Junio C Hamano
parent bdd04b91c3
commit 23633466df
4 changed files with 60 additions and 24 deletions

View File

@ -207,9 +207,9 @@ manpages = {


docs_backend = get_option('docs_backend') docs_backend = get_option('docs_backend')
if docs_backend == 'auto' if docs_backend == 'auto'
if find_program('asciidoc', dirs: program_path, required: false).found() if find_program('asciidoc', dirs: program_path, native: true, required: false).found()
docs_backend = 'asciidoc' docs_backend = 'asciidoc'
elif find_program('asciidoctor', dirs: program_path, required: false).found() elif find_program('asciidoctor', dirs: program_path, native: true, required: false).found()
docs_backend = 'asciidoctor' docs_backend = 'asciidoctor'
else else
error('Neither asciidoc nor asciidoctor were found.') error('Neither asciidoc nor asciidoctor were found.')
@ -217,7 +217,7 @@ if docs_backend == 'auto'
endif endif


if docs_backend == 'asciidoc' if docs_backend == 'asciidoc'
asciidoc = find_program('asciidoc', dirs: program_path) asciidoc = find_program('asciidoc', dirs: program_path, native: true)
asciidoc_html = 'xhtml11' asciidoc_html = 'xhtml11'
asciidoc_docbook = 'docbook' asciidoc_docbook = 'docbook'
xmlto_extra = [ ] xmlto_extra = [ ]
@ -246,7 +246,7 @@ if docs_backend == 'asciidoc'
asciidoc_conf, asciidoc_conf,
] ]
elif docs_backend == 'asciidoctor' elif docs_backend == 'asciidoctor'
asciidoctor = find_program('asciidoctor', dirs: program_path) asciidoctor = find_program('asciidoctor', dirs: program_path, native: true)
asciidoc_html = 'xhtml5' asciidoc_html = 'xhtml5'
asciidoc_docbook = 'docbook5' asciidoc_docbook = 'docbook5'
xmlto_extra = [ xmlto_extra = [
@ -288,7 +288,7 @@ if get_option('breaking_changes')
asciidoc_common_options += ['--attribute', 'with-breaking-changes'] asciidoc_common_options += ['--attribute', 'with-breaking-changes']
endif endif


xmlto = find_program('xmlto', dirs: program_path) xmlto = find_program('xmlto', dirs: program_path, native: true)


cmd_lists = [ cmd_lists = [
'cmds-ancillaryinterrogators.adoc', 'cmds-ancillaryinterrogators.adoc',
@ -409,7 +409,7 @@ if get_option('docs').contains('html')
pointing_to: 'git.html', pointing_to: 'git.html',
) )


xsltproc = find_program('xsltproc', dirs: program_path) xsltproc = find_program('xsltproc', dirs: program_path, native: true)


user_manual_xml = custom_target( user_manual_xml = custom_target(
command: asciidoc_common_options + [ command: asciidoc_common_options + [

View File

@ -1,5 +1,5 @@
gitweb_config = configuration_data() gitweb_config = configuration_data()
gitweb_config.set_quoted('PERL_PATH', perl.full_path()) gitweb_config.set_quoted('PERL_PATH', target_perl.full_path())
gitweb_config.set_quoted('CSSMIN', '') gitweb_config.set_quoted('CSSMIN', '')
gitweb_config.set_quoted('JSMIN', '') gitweb_config.set_quoted('JSMIN', '')
gitweb_config.set_quoted('GIT_BINDIR', get_option('prefix') / get_option('bindir')) gitweb_config.set_quoted('GIT_BINDIR', get_option('prefix') / get_option('bindir'))

View File

@ -155,6 +155,37 @@
# These machine files can be passed to `meson setup` via the `--native-file` # These machine files can be passed to `meson setup` via the `--native-file`
# option. # option.
# #
# Cross compilation
# =================
#
# Machine files can also be used in the context of cross-compilation to
# describe the target machine as well as the cross-compiler toolchain that
# shall be used. An example machine file could look like the following:
#
# [binaries]
# c = 'x86_64-w64-mingw32-gcc'
# cpp = 'x86_64-w64-mingw32-g++'
# ar = 'x86_64-w64-mingw32-ar'
# windres = 'x86_64-w64-mingw32-windres'
# strip = 'x86_64-w64-mingw32-strip'
# exe_wrapper = 'wine64'
# sh = 'C:/Program Files/Git for Windows/usr/bin/sh.exe'
#
# [host_machine]
# system = 'windows'
# cpu_family = 'x86_64'
# cpu = 'x86_64'
# endian = 'little'
#
# These machine files can be passed to `meson setup` via the `--cross-file`
# option.
#
# Note that next to the cross-compiler toolchain, the `[binaries]` section is
# also used to locate a couple of binaries that will be built into Git. This
# includes `sh`, `python` and `perl`, so when cross-compiling Git you likely
# want to set these binary paths in addition to the cross-compiler toolchain
# binaries.
#
# Subproject wrappers # Subproject wrappers
# =================== # ===================
# #
@ -173,7 +204,7 @@ project('git', 'c',
# The version is only of cosmetic nature, so if we cannot find a shell yet we # The version is only of cosmetic nature, so if we cannot find a shell yet we
# simply don't set up a version at all. This may be the case for example on # simply don't set up a version at all. This may be the case for example on
# Windows systems, where we first have to bootstrap the host environment. # Windows systems, where we first have to bootstrap the host environment.
version: find_program('sh', required: false).found() ? run_command( version: find_program('sh', native: true, required: false).found() ? run_command(
'GIT-VERSION-GEN', meson.current_source_dir(), '--format=@GIT_VERSION@', 'GIT-VERSION-GEN', meson.current_source_dir(), '--format=@GIT_VERSION@',
capture: true, capture: true,
check: true, check: true,
@ -198,16 +229,18 @@ elif host_machine.system() == 'windows'
program_path = [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ] program_path = [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ]
endif endif


cygpath = find_program('cygpath', dirs: program_path, required: false) cygpath = find_program('cygpath', dirs: program_path, native: true, required: false)
diff = find_program('diff', dirs: program_path) diff = find_program('diff', dirs: program_path, native: true)
git = find_program('git', dirs: program_path, required: false) git = find_program('git', dirs: program_path, native: true, required: false)
sed = find_program('sed', dirs: program_path) sed = find_program('sed', dirs: program_path, native: true)
shell = find_program('sh', dirs: program_path) shell = find_program('sh', dirs: program_path, native: true)
tar = find_program('tar', dirs: program_path) tar = find_program('tar', dirs: program_path, native: true)

target_shell = find_program('sh', dirs: program_path, native: false)


# Sanity-check that programs required for the build exist. # Sanity-check that programs required for the build exist.
foreach tool : ['cat', 'cut', 'grep', 'sort', 'tr', 'uname'] foreach tool : ['cat', 'cut', 'grep', 'sort', 'tr', 'uname']
find_program(tool, dirs: program_path) find_program(tool, dirs: program_path, native: true)
endforeach endforeach


script_environment = environment() script_environment = environment()
@ -706,7 +739,7 @@ libgit_c_args = [
'-DGIT_LOCALE_PATH="' + get_option('localedir') + '"', '-DGIT_LOCALE_PATH="' + get_option('localedir') + '"',
'-DGIT_MAN_PATH="' + get_option('mandir') + '"', '-DGIT_MAN_PATH="' + get_option('mandir') + '"',
'-DPAGER_ENV="' + get_option('pager_environment') + '"', '-DPAGER_ENV="' + get_option('pager_environment') + '"',
'-DSHELL_PATH="' + fs.as_posix(shell.full_path()) + '"', '-DSHELL_PATH="' + fs.as_posix(target_shell.full_path()) + '"',
] ]
libgit_include_directories = [ '.' ] libgit_include_directories = [ '.' ]
libgit_dependencies = [ ] libgit_dependencies = [ ]
@ -761,6 +794,7 @@ endif
build_options_config.set_quoted('X', executable_suffix) build_options_config.set_quoted('X', executable_suffix)


python = import('python').find_installation('python3', required: get_option('python')) python = import('python').find_installation('python3', required: get_option('python'))
target_python = find_program('python3', native: false, required: python.found())
if python.found() if python.found()
build_options_config.set('NO_PYTHON', '') build_options_config.set('NO_PYTHON', '')
else else
@ -790,9 +824,11 @@ endif
# which we can do starting with Meson 1.5.0 and newer, or we have to # which we can do starting with Meson 1.5.0 and newer, or we have to
# match against the minor version. # match against the minor version.
if meson.version().version_compare('>=1.5.0') if meson.version().version_compare('>=1.5.0')
perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=5.26.0', version_argument: '-V:version') perl = find_program('perl', dirs: program_path, native: true, required: perl_required, version: '>=5.26.0', version_argument: '-V:version')
target_perl = find_program('perl', dirs: program_path, native: false, required: perl.found(), version: '>=5.26.0', version_argument: '-V:version')
else else
perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=26') perl = find_program('perl', dirs: program_path, native: true, required: perl_required, version: '>=26')
target_perl = find_program('perl', dirs: program_path, native: false, required: perl.found(), version: '>=26')
endif endif
perl_features_enabled = perl.found() and get_option('perl').allowed() perl_features_enabled = perl.found() and get_option('perl').allowed()
if perl_features_enabled if perl_features_enabled
@ -843,7 +879,7 @@ else
build_options_config.set('NO_PTHREADS', '1') build_options_config.set('NO_PTHREADS', '1')
endif endif


msgfmt = find_program('msgfmt', dirs: program_path, required: false) msgfmt = find_program('msgfmt', dirs: program_path, native: true, required: false)
gettext_option = get_option('gettext').disable_auto_if(not msgfmt.found()) gettext_option = get_option('gettext').disable_auto_if(not msgfmt.found())
if not msgfmt.found() and gettext_option.enabled() if not msgfmt.found() and gettext_option.enabled()
error('Internationalization via libintl requires msgfmt') error('Internationalization via libintl requires msgfmt')
@ -1974,9 +2010,9 @@ foreach key, value : {
'GIT_TEST_TEMPLATE_DIR': meson.project_build_root() / 'templates', 'GIT_TEST_TEMPLATE_DIR': meson.project_build_root() / 'templates',
'GIT_TEST_TEXTDOMAINDIR': meson.project_build_root() / 'po', 'GIT_TEST_TEXTDOMAINDIR': meson.project_build_root() / 'po',
'PAGER_ENV': get_option('pager_environment'), 'PAGER_ENV': get_option('pager_environment'),
'PERL_PATH': perl.found() ? perl.full_path() : '', 'PERL_PATH': target_perl.found() ? target_perl.full_path() : '',
'PYTHON_PATH': python.found () ? python.full_path() : '', 'PYTHON_PATH': target_python.found () ? target_python.full_path() : '',
'SHELL_PATH': shell.full_path(), 'SHELL_PATH': target_shell.full_path(),
'TAR': tar.full_path(), 'TAR': tar.full_path(),
'TEST_OUTPUT_DIRECTORY': test_output_directory, 'TEST_OUTPUT_DIRECTORY': test_output_directory,
'TEST_SHELL_PATH': shell.full_path(), 'TEST_SHELL_PATH': shell.full_path(),

View File

@ -1,6 +1,6 @@
template_config = configuration_data() template_config = configuration_data()
template_config.set('PERL_PATH', perl.found() ? fs.as_posix(perl.full_path()) : '') template_config.set('PERL_PATH', target_perl.found() ? fs.as_posix(target_perl.full_path()) : '')
template_config.set('SHELL_PATH', fs.as_posix(shell.full_path())) template_config.set('SHELL_PATH', fs.as_posix(target_shell.full_path()))
template_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb')) template_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))


configure_file( configure_file(