find_program: Fallback if version mismatch

Fixes: #11797
pull/11628/head
Xavier Claessens 2 years ago committed by Xavier Claessens
parent 6f87215f1f
commit f720105e24
  1. 70
      mesonbuild/interpreter/interpreter.py
  2. 3
      mesonbuild/modules/__init__.py
  3. 6
      test cases/common/182 find override/meson.build
  4. 3
      test cases/common/182 find override/prog-version.py
  5. 5
      test cases/common/182 find override/subprojects/sub2.wrap
  6. 4
      test cases/common/182 find override/subprojects/sub2/meson.build
  7. 3
      test cases/common/182 find override/subprojects/sub2/prog-version.py

@ -131,6 +131,8 @@ if T.TYPE_CHECKING:
BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]
ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str]
def _project_version_validator(value: T.Union[T.List, str, mesonlib.File, None]) -> T.Optional[str]:
if isinstance(value, list):
@ -1621,46 +1623,23 @@ class Interpreter(InterpreterBase, HoldableObject):
required: bool = True, silent: bool = True,
wanted: T.Union[str, T.List[str]] = '',
search_dirs: T.Optional[T.List[str]] = None,
version_func: T.Optional[T.Callable[[T.Union['ExternalProgram', 'build.Executable', 'OverrideProgram']], str]] = None
version_func: T.Optional[ProgramVersionFunc] = None
) -> T.Union['ExternalProgram', 'build.Executable', 'OverrideProgram']:
args = mesonlib.listify(args)
extra_info: T.List[mlog.TV_Loggable] = []
progobj = self.program_lookup(args, for_machine, default_options, required, search_dirs, extra_info)
if progobj is None:
progobj = self.program_lookup(args, for_machine, default_options, required, search_dirs, wanted, version_func, extra_info)
if progobj is None or not self.check_program_version(progobj, wanted, version_func, extra_info):
progobj = self.notfound_program(args)
if isinstance(progobj, ExternalProgram) and not progobj.found():
if not silent:
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'))
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), *extra_info)
if required:
m = 'Program {!r} not found or not executable'
raise InterpreterException(m.format(progobj.get_name()))
return progobj
if wanted:
if version_func:
version = version_func(progobj)
elif isinstance(progobj, build.Executable):
if progobj.subproject:
interp = self.subprojects[progobj.subproject].held_object
else:
interp = self
assert isinstance(interp, Interpreter)
version = interp.project_version
else:
version = progobj.get_version(self)
is_found, not_found, _ = mesonlib.version_compare_many(version, wanted)
if not is_found:
mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.red('NO'),
'found', mlog.normal_cyan(version), 'but need:',
mlog.bold(', '.join([f"'{e}'" for e in not_found])), *extra_info)
if required:
m = 'Invalid version of program, need {!r} {!r} found {!r}.'
raise InterpreterException(m.format(progobj.name, not_found, version))
return self.notfound_program(args)
extra_info.insert(0, mlog.normal_cyan(version))
# Only store successful lookups
self.store_name_lookups(args)
if not silent:
@ -1671,7 +1650,11 @@ class Interpreter(InterpreterBase, HoldableObject):
def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: MachineChoice,
default_options: T.Optional[T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]]],
required: bool, search_dirs: T.List[str], extra_info: T.List[mlog.TV_Loggable]
required: bool,
search_dirs: T.List[str],
wanted: T.Union[str, T.List[str]],
version_func: T.Optional[ProgramVersionFunc],
extra_info: T.List[mlog.TV_Loggable]
) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]:
progobj = self.program_from_overrides(args, extra_info)
if progobj:
@ -1694,11 +1677,42 @@ class Interpreter(InterpreterBase, HoldableObject):
if progobj is None and args[0].endswith('python3'):
prog = ExternalProgram('python3', mesonlib.python_command, silent=True)
progobj = prog if prog.found() else None
if progobj and not self.check_program_version(progobj, wanted, version_func, extra_info):
progobj = None
if progobj is None and fallback and required:
progobj = self.notfound_program(args)
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), *extra_info)
extra_info.clear()
progobj = self.find_program_fallback(fallback, args, default_options, required, extra_info)
return progobj
def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram],
wanted: T.Union[str, T.List[str]],
version_func: T.Optional[ProgramVersionFunc],
extra_info: T.List[mlog.TV_Loggable]) -> bool:
if wanted:
if version_func:
version = version_func(progobj)
elif isinstance(progobj, build.Executable):
if progobj.subproject:
interp = self.subprojects[progobj.subproject].held_object
else:
interp = self
assert isinstance(interp, Interpreter)
version = interp.project_version
else:
version = progobj.get_version(self)
is_found, not_found, _ = mesonlib.version_compare_many(version, wanted)
if not is_found:
extra_info[:0] = ['found', mlog.normal_cyan(version), 'but need:',
mlog.bold(', '.join([f"'{e}'" for e in not_found]))]
return False
extra_info.insert(0, mlog.normal_cyan(version))
return True
def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString],
default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]],
required: bool, extra_info: T.List[mlog.TV_Loggable]

@ -26,6 +26,7 @@ from ..programs import ExternalProgram
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
from ..interpreter.interpreter import ProgramVersionFunc
from ..interpreter.interpreterobjects import MachineHolder
from ..interpreterbase import TYPE_var, TYPE_kwargs
from ..programs import OverrideProgram
@ -86,7 +87,7 @@ class ModuleState:
def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.FileOrString]],
required: bool = True,
version_func: T.Optional[T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str]] = None,
version_func: T.Optional[ProgramVersionFunc] = None,
wanted: T.Optional[str] = None, silent: bool = False,
for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.Executable, OverrideProgram]:
if not isinstance(prog, list):

@ -23,3 +23,9 @@ six_prog = find_program('six_meson_exe')
assert(six_prog.found())
assert(six_prog.full_path() != '')
assert(six_prog.full_path() == six_prog.path())
# We have prog-version.py in current directory, but it's version 1.0.
# This needs to use fallback for "prog-version" name which will be version 2.0.
prog = find_program('prog-version.py', 'prog-version', version: '>= 2.0')
assert(prog.found())
assert(prog.version() == '2.0')

@ -0,0 +1,3 @@
#! /usr/bin/env python3
print('1.0')

@ -0,0 +1,5 @@
[wrap-file]
directory = sub2
[provide]
program_names = prog-version

@ -0,0 +1,4 @@
project('sub2')
prog = find_program('prog-version.py')
meson.override_find_program('prog-version', prog)
Loading…
Cancel
Save