Add version check support to find_program()

Closes: #1609
pull/5689/head
Xavier Claessens 5 years ago committed by Xavier Claessens
parent 43aae8243c
commit 2e41d53e4d
  1. 8
      docs/markdown/Reference-manual.md
  2. 9
      docs/markdown/snippets/find_program_version.md
  3. 38
      mesonbuild/interpreter.py
  4. 9
      test cases/common/29 find program/meson.build
  5. 8
      test cases/common/29 find program/print-version-with-prefix.py
  6. 8
      test cases/common/29 find program/print-version.py

@ -671,6 +671,14 @@ Keyword arguments are the following:
[disabler object](#disabler-object) instead of a not-found object.
*Since 0.49.0*
- `version` *(since 0.52.0)* Specifies the required version, see
[`dependency()`](#dependency) for argument format. 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 occurence 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()`](#run_command).
Meson will also autodetect scripts with a shebang line and run them
with the executable/interpreter specified in it both on Windows
(because the command invocator will reject the command otherwise) and

@ -0,0 +1,9 @@
## Version check in `find_program()`
A new `version` keyword argument has been added to `find_program` to specify
the required version. See [`dependency()`](#dependency) for argument format.
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 occurence 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()`](#run_command).

@ -489,6 +489,7 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder):
ObjectHolder.__init__(self, ep)
self.methods.update({'found': self.found_method,
'path': self.path_method})
self.cached_version = None
@noPosargs
@permittedKwargs({})
@ -509,6 +510,24 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder):
def get_name(self):
return self.held_object.get_name()
def get_version(self, interpreter):
if not self.cached_version:
raw_cmd = self.get_command() + ['--version']
cmd = [self, '--version']
res = interpreter.run_command_impl(interpreter.current_node, cmd, {}, True)
if res.returncode != 0:
m = 'Running {!r} failed'
raise InterpreterException(m.format(raw_cmd))
output = res.stdout.strip()
if not output:
output = res.stderr.strip()
match = re.search(r'([0-9\.]+)', output)
if not match:
m = 'Could not find a version number in output of {!r}'
raise InterpreterException(m.format(raw_cmd))
self.cached_version = match.group(1)
return self.cached_version
class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
def __init__(self, el, pv):
InterpreterObject.__init__(self)
@ -1993,7 +2012,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'version',
},
'executable': build.known_exe_kwargs,
'find_program': {'required', 'native'},
'find_program': {'required', 'native', 'version'},
'generator': {'arguments',
'output',
'depends',
@ -2879,7 +2898,7 @@ external dependencies (including libraries) must go to "dependencies".''')
# TODO update modules to always pass `for_machine`. It is bad-form to assume
# the host machine.
def find_program_impl(self, args, for_machine: MachineChoice = MachineChoice.HOST, required=True, silent=True):
def find_program_impl(self, args, for_machine: MachineChoice = MachineChoice.HOST, required=True, silent=True, wanted=''):
if not isinstance(args, list):
args = [args]
@ -2897,8 +2916,20 @@ external dependencies (including libraries) must go to "dependencies".''')
return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
# Only store successful lookups
self.store_name_lookups(args)
if wanted:
version = progobj.get_version(self)
is_found, not_found, found = mesonlib.version_compare_many(version, wanted)
if not is_found:
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'),
'found {!r} but need:'.format(version),
', '.join(["'{}'".format(e) for e in not_found]))
if required:
m = 'Invalid version of program, need {!r} {!r} found {!r}.'
raise InvalidArguments(m.format(progobj.get_name(), not_found, version))
return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
return progobj
@FeatureNewKwargs('find_program', '0.52.0', ['version'])
@FeatureNewKwargs('find_program', '0.49.0', ['disabler'])
@disablerIfNotFound
@permittedKwargs(permitted_kwargs['find_program'])
@ -2913,8 +2944,9 @@ external dependencies (including libraries) must go to "dependencies".''')
if not isinstance(required, bool):
raise InvalidArguments('"required" argument must be a boolean.')
wanted = mesonlib.stringlistify(kwargs.get('version', []))
for_machine = self.machine_from_native_kwarg(kwargs)
return self.find_program_impl(args, for_machine, required=required, silent=False)
return self.find_program_impl(args, for_machine, required=required, silent=False, wanted=wanted)
def func_find_library(self, node, args, kwargs):
raise InvalidCode('find_library() is removed, use meson.get_compiler(\'name\').find_library() instead.\n'

@ -18,3 +18,12 @@ else
e = executable('prog', generated)
test('external exe', e)
endif
prog = find_program('print-version.py', version : '>=2.0', required : false)
assert(not prog.found(), 'Version should be too old')
prog = find_program('print-version.py', version : '>=1.0')
assert(prog.found(), 'Program version should match')
prog = find_program('print-version-with-prefix.py', version : '>=1.0')
assert(prog.found(), 'Program version should match')

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

@ -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