diff options
-rw-r--r-- | docs/markdown/Reference-manual.md | 3 | ||||
-rw-r--r-- | docs/markdown/snippets/find_program.md | 9 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 11 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 41 | ||||
-rw-r--r-- | test cases/common/28 find program/meson.build | 6 | ||||
-rw-r--r-- | test cases/common/28 find program/scripts/test_subdir.py | 3 |
6 files changed, 56 insertions, 17 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index dd0b3f4..9762955 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -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 diff --git a/docs/markdown/snippets/find_program.md b/docs/markdown/snippets/find_program.md new file mode 100644 index 0000000..2bef824 --- /dev/null +++ b/docs/markdown/snippets/find_program.md @@ -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']) +``` diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index d11aebf..258a4fb 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -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 diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index e402afb..d3a40f7 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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' diff --git a/test cases/common/28 find program/meson.build b/test cases/common/28 find program/meson.build index 983b7b4..3b59caa 100644 --- a/test cases/common/28 find program/meson.build +++ b/test cases/common/28 find program/meson.build @@ -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') diff --git a/test cases/common/28 find program/scripts/test_subdir.py b/test cases/common/28 find program/scripts/test_subdir.py new file mode 100644 index 0000000..947ffe4 --- /dev/null +++ b/test cases/common/28 find program/scripts/test_subdir.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +exit(0) |