override_find_program: allow overriding multiple times with the same executable

override_find_program collapses `native: true` to `native: false` when
not cross compiling.  When wishing to add an override for both machines,
for example because a program has machine-independent output, this
causes the same (name, machine) pair to be overridden twice.  As a
special exception allow repeated override_find_program when the
executable is the same.

Add documentation about how to call override_find_program properly
for such a program.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini authored and Paolo Bonzini committed 2026-10-30 14:52:37 +01:00
1 parent e323ad224b
commit 9684d6b922
12 files changed
+107 -4

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
## `meson.override_find_program` can be called multiple times for the same name and program
Meson now allows overriding the same program twice if both calls refer
to the same executable. This makes it easier to override a program
for both `native: true` and `native: false`, if its output is
target-independent.
+26
View File
@@ -386,6 +386,32 @@ methods:
*(since 1.12.0)* A program can be overridden both for the host machine and for the
build machine.
Using `override_find_program` when cross compiling requires some care. In particular:
- a program built for the host machine should only be registered if the host
binaries can be run, otherwise it cannot be executed.
- *since 1.13.0* Meson allows overriding the same program twice if both calls refer
to the same executable, but this may not work when cross compiling if a
subproject is invoked for both the host and build machine.
`meson.can_run_host_binaries()` and `meson.is_cross_build()` make it possible
to override the program for both cases:
```meson
if meson.can_run_host_binaries()
meson.override_find_program('mytool', mytool_exe)
# Add a second override if the output of the tool is machine-independent.
# In a cross build, the copy of the subproject for the build machine
# provides the program for the build machine.
if not meson.is_cross_build() and meson.version().version_compare('>=1.13.0')
meson.override_find_program('mytool', mytool_exe, native : true)
endif
endif
```
See [[find_program]] for how to use an overridden program.
posargs:
progname:
type: str
+14
View File
@@ -3731,6 +3731,20 @@ class LocalProgram(programs.Program):
self.program = program
self.version = version
def __eq__(self, other: object) -> bool:
if not isinstance(other, LocalProgram):
return False
if isinstance(self.program, programs.ExternalProgram):
return isinstance(other.program, programs.ExternalProgram) and self.file == other.file
else:
return self.program is other.program
def __hash__(self) -> int:
if isinstance(self.program, programs.ExternalProgram):
return hash(self.file)
else:
return hash(self.program)
def found(self) -> bool:
return True
+10 -1
View File
@@ -1768,7 +1768,16 @@ class Interpreter(InterpreterBase, HoldableObject):
def add_find_program_override(self, name: str, exe: Program, for_machine: MachineChoice) -> None:
if name in self.build.searched_programs[for_machine]:
raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.')
if name in self.build.find_overrides[for_machine]:
previous = self.build.find_overrides[for_machine].get(name)
if previous is not None:
# For native builds, both machines map to MachineChoice.HOST. Allow
# overriding the same program with the same executable, so that it is
# possible to call meson.override_find_program consecutively with
# `native: true` and `native: false`.
if previous == exe:
FeatureNew.single_use('Overriding the same program multiple times (with the same executable)',
'1.13.0', self.subproject)
return
raise InterpreterException(f'Tried to override executable "{name}" which has already been overridden.')
self.build.find_overrides[for_machine][name] = exe
if name == 'pkg-config' and isinstance(exe, ExternalProgram):
@@ -1,5 +1,6 @@
project('yo dawg')
p = find_program('overrides.py')
meson.override_find_program('override', p)
p = find_program('override1.py')
q = find_program('override2.py')
meson.override_find_program('override', p)
meson.override_find_program('override', q)
@@ -0,0 +1,4 @@
#!/usr/bin/env python3
print('Yo dawg, we put overrides in your overrides,')
print('so now you can override when you override.')
@@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/65 dual override/meson.build:5:6: ERROR: Tried to override executable \"override\" which has already been overridden."
"line": "test cases/failing/65 dual override/meson.build:6:6: ERROR: Tried to override executable \"override\" which has already been overridden."
}
]
}
@@ -0,0 +1,22 @@
project('override for both machines', 'c', meson_version : '>= 1.12.0')
subproject('tools')
subproject('tools', native : true)
tool = find_program('mytool', required: false)
if not tool.found()
# If a tool produces machine-independent output, it can be
# looked up with `native: true`.
tool = find_program('mytool', native : true)
endif
ct = custom_target(
'generated',
command : [tool],
output : 'generated.txt',
capture : true,
build_by_default : true,
)
e = executable('prog', 'prog.c')
test('prog', e)
@@ -0,0 +1,3 @@
int main(void) {
return 0;
}
@@ -0,0 +1,12 @@
project('tools', 'c')
exe = executable('mytool', 'mytool.c')
if meson.can_run_host_binaries()
meson.override_find_program('mytool', exe)
# In a cross build, the copy of the subproject for the build machine provides
# the program for the build machine.
if not meson.is_cross_build()
meson.override_find_program('mytool', exe, native : true)
endif
endif
@@ -0,0 +1,6 @@
#include <stdio.h>
int main(void) {
printf("generated\n");
return 0;
}