find_program(): Add 'dirs' keyword argument

Fixes: #1576
pull/6301/head
Xavier Claessens 5 years ago committed by Xavier Claessens
parent 691a74aec2
commit b872099468
  1. 3
      docs/markdown/Reference-manual.md
  2. 9
      docs/markdown/snippets/find_program.md
  3. 11
      mesonbuild/dependencies/base.py
  4. 41
      mesonbuild/interpreter.py
  5. 6
      test cases/common/28 find program/meson.build
  6. 3
      test cases/common/28 find program/scripts/test_subdir.py

@ -701,6 +701,9 @@ Keyword arguments are the following:
If the output is more complicated than that, the version checking will have to
be done manually using [`run_command()`](#run_command).
- `dirs` *(since 0.53.0)* Extra list of absolute paths where to look for program
names.
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 @@
## Search directories for `find_program()`
It is now possible to give a list of absolute paths where `find_program()` should
also search, using the `dirs` keyword argument.
For example on Linux `/sbin` and `/usr/sbin` are not always in the `$PATH`:
```meson
prog = find_program('mytool', dirs : ['/usr/sbin', '/sbin'])
```

@ -1699,12 +1699,19 @@ class ExternalProgram:
for_machine = MachineChoice.BUILD
def __init__(self, name: str, command: Optional[List[str]] = None,
silent: bool = False, search_dir: Optional[str] = None):
silent: bool = False, search_dir: Optional[str] = None,
extra_search_dirs: Optional[List[str]] = None):
self.name = name
if command is not None:
self.command = listify(command)
else:
self.command = self._search(name, search_dir)
all_search_dirs = [search_dir]
if extra_search_dirs:
all_search_dirs += extra_search_dirs
for d in all_search_dirs:
self.command = self._search(name, d)
if self.found():
break
# Set path to be the last item that is actually a file (in order to
# skip options in something like ['python', '-u', 'file.py']. If we

@ -119,6 +119,18 @@ def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True)
return disabled, required, feature
def extract_search_dirs(kwargs):
search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
search_dirs = [Path(d).expanduser() for d in search_dirs]
for d in search_dirs:
if mesonlib.is_windows() and d.root.startswith('\\'):
# a Unix-path starting with `/` that is not absolute on Windows.
# discard without failing for end-user ease of cross-platform directory arrays
continue
if not d.is_absolute():
raise InvalidCode('Search directory {} is not an absolute path.'.format(d))
return list(map(str, search_dirs))
class TryRunResultHolder(InterpreterObject):
def __init__(self, res):
super().__init__()
@ -1554,16 +1566,7 @@ class CompilerHolder(InterpreterObject):
if not self.has_header_method([h], has_header_kwargs):
return self.notfound_library(libname)
search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
search_dirs = [Path(d).expanduser() for d in search_dirs]
for d in search_dirs:
if mesonlib.is_windows() and d.root.startswith('\\'):
# a Unix-path starting with `/` that is not absolute on Windows.
# discard without failing for end-user ease of cross-platform directory arrays
continue
if not d.is_absolute():
raise InvalidCode('Search directory {} is not an absolute path.'.format(d))
search_dirs = list(map(str, search_dirs))
search_dirs = extract_search_dirs(kwargs)
libtype = mesonlib.LibType.PREFER_SHARED
if 'static' in kwargs:
@ -2036,7 +2039,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'version',
},
'executable': build.known_exe_kwargs,
'find_program': {'required', 'native', 'version'},
'find_program': {'required', 'native', 'version', 'dirs'},
'generator': {'arguments',
'output',
'depends',
@ -2896,7 +2899,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return ExternalProgramHolder(prog)
return None
def program_from_system(self, args, silent=False):
def program_from_system(self, args, search_dirs, silent=False):
# Search for scripts relative to current subdir.
# Do not cache found programs because find_program('foobar')
# might give different results when run from different source dirs.
@ -2910,12 +2913,15 @@ external dependencies (including libraries) must go to "dependencies".''')
search_dir = os.path.join(self.environment.get_source_dir(),
exename.subdir)
exename = exename.fname
extra_search_dirs = []
elif isinstance(exename, str):
search_dir = source_dir
extra_search_dirs = search_dirs
else:
raise InvalidArguments('find_program only accepts strings and '
'files, not {!r}'.format(exename))
extprog = dependencies.ExternalProgram(exename, search_dir=search_dir,
extra_search_dirs=extra_search_dirs,
silent=silent)
progobj = ExternalProgramHolder(extprog)
if progobj.found():
@ -2949,7 +2955,8 @@ 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, wanted=''):
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]
@ -2957,7 +2964,7 @@ external dependencies (including libraries) must go to "dependencies".''')
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, silent=silent)
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)
@ -2980,6 +2987,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
return progobj
@FeatureNewKwargs('find_program', '0.53.0', ['dirs'])
@FeatureNewKwargs('find_program', '0.52.0', ['version'])
@FeatureNewKwargs('find_program', '0.49.0', ['disabler'])
@disablerIfNotFound
@ -2993,9 +3001,12 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled')
return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
search_dirs = extract_search_dirs(kwargs)
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, wanted=wanted)
return self.find_program_impl(args, for_machine, required=required,
silent=False, wanted=wanted,
search_dirs=search_dirs)
def func_find_library(self, node, args, kwargs):
raise InvalidCode('find_library() is removed, use meson.get_compiler(\'name\').find_library() instead.\n'

@ -27,3 +27,9 @@ 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')
prog = find_program('test_subdir.py', required : false)
assert(not prog.found(), 'Program should not be found')
prog = find_program('test_subdir.py', dirs : ['/donotexist', meson.current_source_dir() / 'scripts'])
assert(prog.found(), 'Program should be found')

Loading…
Cancel
Save