find_program: Fallback if a wrap file provide the program name

We don't need the legacy variable name system as for dependency()
fallbacks because meson.override_find_program() is largely used already,
so we can just rely on it.
pull/6902/head
Xavier Claessens 5 years ago
parent 288d1ae5a5
commit f08eed37cb
  1. 13
      docs/markdown/Wrap-dependency-system-manual.md
  2. 5
      docs/markdown/snippets/implicit_fallback.md
  3. 83
      mesonbuild/interpreter.py
  4. 14
      mesonbuild/wrap/wrap.py
  5. 3
      test cases/common/187 find override/meson.build
  6. 5
      test cases/common/187 find override/subprojects/sub.wrap
  7. 4
      test cases/common/187 find override/subprojects/sub/meson.build

@ -71,6 +71,7 @@ revision = head
- `directory` - name of the subproject root directory, defaults to the name of the wrap.
Since *0.55.0* those can be used in all wrap types, they were previously reserved to `wrap-file`:
- `patch_url` - download url to retrieve an optional overlay archive
- `patch_fallback_url` - fallback URL to be used when download from `patch_url` fails *Since: 0.55.0*
- `patch_filename` - filename of the downloaded overlay archive
@ -182,6 +183,18 @@ dependency_names = glib-2.0, gobject-2.0, gio-2.0
With such wrap file, `dependency('glib-2.0')` will automatically fallback to use
`glib.wrap` and return `glib_dep` variable from the subproject.
Programs can also be provided by wrap files, with the `program_names` key:
```ini
[wrap-git]
...
[provide]
program_names = myprog, otherprog
```
With such wrap file, `find_program('myprog')` will automatically fallback to use
the subproject, assuming it uses `meson.override_find_program('myprog')`.
## Using wrapped projects
Wraps provide a convenient way of obtaining a project into your subproject directory.

@ -14,3 +14,8 @@ Wrap files can define the dependencies it provides in the `[provide]` section.
When a wrap file provides the dependency `foo` any call do `dependency('foo')`
will automatically fallback to that subproject even if no `fallback` keyword
argument is given. See [Wrap documentation](Wrap-dependency-system-manual.md#provide_section).
## `find_program()` fallback
When a program cannot be found on the system but a wrap file has its name in the
`[provide]` section, that subproject will be used as fallback.

@ -3251,7 +3251,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return success
def program_from_file_for(self, for_machine, prognames, silent):
def program_from_file_for(self, for_machine, prognames):
for p in unholder(prognames):
if isinstance(p, mesonlib.File):
continue # Always points to a local (i.e. self generated) file.
@ -3290,15 +3290,13 @@ external dependencies (including libraries) must go to "dependencies".''')
if progobj.found():
return progobj
def program_from_overrides(self, command_names, silent=False):
def program_from_overrides(self, command_names, extra_info):
for name in command_names:
if not isinstance(name, str):
continue
if name in self.build.find_overrides:
exe = self.build.find_overrides[name]
if not silent:
mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
'(overridden: %s)' % exe.description())
extra_info.append(mlog.blue('(overriden)'))
return ExternalProgramHolder(exe, self.subproject, self.backend)
return None
@ -3316,40 +3314,75 @@ external dependencies (including libraries) must go to "dependencies".''')
% name)
self.build.find_overrides[name] = exe
def notfound_program(self, args):
return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject)
# 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, wanted='', search_dirs=None):
if not isinstance(args, list):
args = [args]
args = mesonlib.listify(args)
progobj = self.program_from_overrides(args, silent=silent)
if progobj is None:
progobj = self.program_from_file_for(for_machine, args, silent=silent)
if progobj is None:
progobj = self.program_from_system(args, search_dirs, silent=silent)
if progobj is None and args[0].endswith('python3'):
prog = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True)
progobj = ExternalProgramHolder(prog, self.subproject)
if required and (progobj is None or not progobj.found()):
raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args))
extra_info = []
progobj = self.program_lookup(args, for_machine, required, search_dirs, extra_info)
if progobj is None:
return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject)
# Only store successful lookups
self.store_name_lookups(args)
progobj = self.notfound_program(args)
if not progobj.found():
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'))
if required:
m = 'Program {!r} not found'
raise InterpreterException(m.format(progobj.get_name()))
return progobj
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]))
'found', mlog.normal_cyan(version), 'but need:',
mlog.bold(', '.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(' '.join(args)), self.subproject)
raise InterpreterException(m.format(progobj.get_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)
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.green('YES'), *extra_info)
return progobj
def program_lookup(self, args, for_machine, required, search_dirs, extra_info):
progobj = self.program_from_overrides(args, extra_info)
if progobj:
return progobj
fallback = None
wrap_mode = self.coredata.get_builtin_option('wrap_mode')
if wrap_mode != WrapMode.nofallback:
fallback = self.environment.wrap_resolver.find_program_provider(args)
if fallback and wrap_mode == WrapMode.forcefallback:
return self.find_program_fallback(fallback, args, required, extra_info)
progobj = self.program_from_file_for(for_machine, args)
if progobj is None:
progobj = self.program_from_system(args, search_dirs, silent=True)
if progobj is None and args[0].endswith('python3'):
prog = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True)
progobj = ExternalProgramHolder(prog, self.subproject) if prog.found() else None
if progobj is None and fallback and required:
progobj = self.find_program_fallback(fallback, args, required, extra_info)
return progobj
def find_program_fallback(self, fallback, args, required, extra_info):
mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program',
mlog.bold(' '.join(args)))
sp_kwargs = { 'required': required }
self.do_subproject(fallback, 'meson', sp_kwargs)
return self.program_from_overrides(args, extra_info)
@FeatureNewKwargs('find_program', '0.53.0', ['dirs'])
@FeatureNewKwargs('find_program', '0.52.0', ['version'])
@FeatureNewKwargs('find_program', '0.49.0', ['disabler'])
@ -3362,7 +3395,7 @@ external dependencies (including libraries) must go to "dependencies".''')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled')
return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject)
return self.notfound_program(args)
search_dirs = extract_search_dirs(kwargs)
wanted = mesonlib.stringlistify(kwargs.get('version', []))

@ -126,6 +126,7 @@ class PackageDefinition:
def parse_provide_section(self):
self.provide = {self.name: None}
self.provide_programs = []
if self.config.has_section('provide'):
for k, v in self.config['provide'].items():
if k == 'dependency_names':
@ -134,6 +135,11 @@ class PackageDefinition:
names = {n.strip(): None for n in v.split(',')}
self.provide.update(names)
continue
if k == 'program_names':
# A coma separated list of program names
names = {n.strip(): None for n in v.split(',')}
self.provide_programs += names
continue
if not v:
m = ('Empty dependency variable name for {!r} in {}. '
'If the subproject uses meson.override_dependency() '
@ -199,6 +205,14 @@ class Resolver:
return wrap.name
return directory
def find_program_provider(self, names: T.List[str]):
wraps = [i[0] for i in self.wraps.values()]
for name in names:
for wrap in wraps:
if wrap and name in wrap.provide_programs:
return wrap.name
return None
def resolve(self, packagename: str, method: str, current_subproject: str = '') -> str:
self.current_subproject = current_subproject
self.packagename = packagename

@ -10,3 +10,6 @@ if not gencodegen.found()
endif
subdir('otherdir')
tool = find_program('sometool')
assert(tool.found())

@ -0,0 +1,5 @@
[wrap-file]
directory = sub
[provide]
program_names = sometool

@ -0,0 +1,4 @@
project('tools')
exe = find_program('gencodegen')
meson.override_find_program('sometool', exe)
Loading…
Cancel
Save