find_program: add a kwarg to specify custom version argument

When trying to get the version of a program, meson was previously
hardcoded to run the binary with `--version`. This does work with the
vast majority of programs, but there are a few outliers (e.g. ffmpeg)
which have an unusual argument for printing out the version. Support
these programs by introducing a version_argument kwarg in find_program
which allows users to override `--version` with whatever the custom
argument for printing the version may be for the program.
pull/13075/merge
Dudemanguy 9 months ago committed by Jussi Pakkanen
parent a111c28ece
commit 9be6e653d4
  1. 12
      docs/markdown/snippets/find_program_version_argument.md
  2. 9
      docs/yaml/functions/find_program.yaml
  3. 1
      mesonbuild/dependencies/python.py
  4. 9
      mesonbuild/interpreter/interpreter.py
  5. 5
      mesonbuild/programs.py
  6. 3
      test cases/common/26 find program/meson.build
  7. 8
      test cases/common/26 find program/print-version-custom-argument.py

@ -0,0 +1,12 @@
## New version_argument kwarg for find_program
When finding an external program with `find_program`, the `version_argument`
can be used to override the default `--version` argument when trying to parse
the version of the program.
For example, if the following is used:
```meson
foo = find_program('foo', version_argument: '-version')
```
meson will internally run `foo -version` when trying to find the version of `foo`.

@ -102,13 +102,20 @@ kwargs:
since: 0.52.0
description: |
Specifies the required version, see
[[dependency]] for argument format. The version of the program
[[dependency]] for argument format. By default, the version of the program
is determined by running `program_name --version` command. If stdout is empty
it fallbacks to stderr. If the output contains more text than simply a version
number, only the first occurrence of numbers separated by dots is kept.
If the output is more complicated than that, the version checking will have to
be done manually using [[run_command]].
version_argument:
type: str
since: 1.5.0
description: |
Specifies the argument to pass when trying to find the version of the program.
If this is unspecified, `program_name --version` will be used.
dirs:
type: list[str]
since: 0.53.0

@ -83,6 +83,7 @@ class BasicPythonExternalProgram(ExternalProgram):
self.command = ext_prog.command
self.path = ext_prog.path
self.cached_version = None
self.version_arg = '--version'
# We want strong key values, so we always populate this with bogus data.
# Otherwise to make the type checkers happy we'd have to do .get() for

@ -1642,12 +1642,13 @@ 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_arg: T.Optional[str] = '',
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, wanted, version_func, extra_info)
progobj = self.program_lookup(args, for_machine, default_options, required, search_dirs, wanted, version_arg, 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)
@ -1672,6 +1673,7 @@ class Interpreter(InterpreterBase, HoldableObject):
required: bool,
search_dirs: T.List[str],
wanted: T.Union[str, T.List[str]],
version_arg: T.Optional[str],
version_func: T.Optional[ProgramVersionFunc],
extra_info: T.List[mlog.TV_Loggable]
) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]:
@ -1697,6 +1699,8 @@ class Interpreter(InterpreterBase, HoldableObject):
prog = ExternalProgram('python3', mesonlib.python_command, silent=True)
progobj = prog if prog.found() else None
if isinstance(progobj, ExternalProgram) and version_arg:
progobj.version_arg = version_arg
if progobj and not self.check_program_version(progobj, wanted, version_func, extra_info):
progobj = None
@ -1756,6 +1760,7 @@ class Interpreter(InterpreterBase, HoldableObject):
REQUIRED_KW,
KwargInfo('dirs', ContainerTypeInfo(list, str), default=[], listify=True, since='0.53.0'),
KwargInfo('version', ContainerTypeInfo(list, str), default=[], listify=True, since='0.52.0'),
KwargInfo('version_argument', str, default='', since='1.5.0'),
DEFAULT_OPTIONS.evolve(since='1.3.0')
)
@disablerIfNotFound
@ -1770,7 +1775,7 @@ class Interpreter(InterpreterBase, HoldableObject):
search_dirs = extract_search_dirs(kwargs)
default_options = kwargs['default_options']
return self.find_program_impl(args[0], kwargs['native'], default_options=default_options, required=required,
silent=False, wanted=kwargs['version'],
silent=False, wanted=kwargs['version'], version_arg=kwargs['version_argument'],
search_dirs=search_dirs)
# When adding kwargs, please check if they make sense in dependencies.get_dep_identifier()

@ -36,6 +36,7 @@ class ExternalProgram(mesonlib.HoldableObject):
self.name = name
self.path: T.Optional[str] = None
self.cached_version: T.Optional[str] = None
self.version_arg = '--version'
if command is not None:
self.command = mesonlib.listify(command)
if mesonlib.is_windows():
@ -93,9 +94,9 @@ class ExternalProgram(mesonlib.HoldableObject):
def get_version(self, interpreter: T.Optional['Interpreter'] = None) -> str:
if not self.cached_version:
raw_cmd = self.get_command() + ['--version']
raw_cmd = self.get_command() + [self.version_arg]
if interpreter:
res = interpreter.run_command_impl((self, ['--version']),
res = interpreter.run_command_impl((self, [self.version_arg]),
{'capture': True,
'check': True,
'env': mesonlib.EnvironmentVariables()},

@ -32,6 +32,9 @@ assert(prog.version() == '1.0', 'Program version should be detectable')
prog = find_program('print-version-with-prefix.py', version : '>=1.0')
assert(prog.found(), 'Program version should match')
prog = find_program('print-version-custom-argument.py', version : '>=1.0', version_argument : '-version')
assert(prog.found(), 'Program version should match')
prog = find_program('test_subdir.py', required : false)
assert(not prog.found(), 'Program should not be found')

@ -0,0 +1,8 @@
#!/usr/bin/env python3
import sys
if len(sys.argv) != 2 or sys.argv[1] != '-version':
exit(1)
print('1.0')
Loading…
Cancel
Save