diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2020-04-20 16:23:33 -0400 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2020-04-28 01:39:56 +0300 |
commit | 39a69d1fb0e130fae9f64b81e0a992503869a97a (patch) | |
tree | 2f9cbed234a6d55ba033acda49ec438e7c34fa3a | |
parent | 34e7e8780c0196313be8700f504ec84fd6cba3d1 (diff) | |
download | meson-39a69d1fb0e130fae9f64b81e0a992503869a97a.zip meson-39a69d1fb0e130fae9f64b81e0a992503869a97a.tar.gz meson-39a69d1fb0e130fae9f64b81e0a992503869a97a.tar.bz2 |
find_program: Fixes when the program has been overridden by executable
- ExternalProgramHolder has path() method while CustomTargetHolder and
BuildTargetHolder have full_path().
- The returned ExternalProgramHolder's path() method was broken, because
build.Executable object has no get_path() method, it needs the
backend.
- find_program('overridden_prog', version : '>=1.0') was broken because
it needs to execute the exe that is not yet built. Now assume the
program has the (sub)project version.
- If the version check fails, interpreter uses
ExternalProgramHolder.get_name() for the error message but
build.Executable does not implement get_name() method.
-rw-r--r-- | docs/markdown/Reference-manual.md | 10 | ||||
-rw-r--r-- | docs/markdown/snippets/find_program.md | 20 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 52 | ||||
-rw-r--r-- | mesonbuild/modules/python.py | 10 | ||||
-rw-r--r-- | test cases/common/201 override with exe/meson.build | 8 | ||||
-rw-r--r-- | test cases/common/201 override with exe/subprojects/sub/meson.build | 2 |
6 files changed, 79 insertions, 23 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 5156b5b..963af9d 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1846,7 +1846,9 @@ the following methods. specifies that whenever `find_program` is used to find a program named `progname`, Meson should not look it up on the system but instead return `program`, which may either be the result of - `find_program`, `configure_file` or `executable`. + `find_program`, `configure_file` or `executable`. *Since 0.55.0* if a version + check is passed to `find_program` for a program that has been overridden with + an executable, the current project version is used. If `program` is an `executable`, it cannot be used during configure. @@ -2460,6 +2462,12 @@ and has the following methods: - `path()` which returns a string pointing to the script or executable **NOTE:** You should not need to use this method. Passing the object itself should work in all cases. For example: `run_command(obj, arg1, arg2)` + *Since 0.55.0* this method has been deprecated in favor of `full_path()` for + consistency with other returned objects. + +- `full_path()` *Since 0.55.0* which returns a string pointing to the script or + executable **NOTE:** You should not need to use this method. Passing the object + itself should work in all cases. For example: `run_command(obj, arg1, arg2)`. ### `environment` object diff --git a/docs/markdown/snippets/find_program.md b/docs/markdown/snippets/find_program.md new file mode 100644 index 0000000..d0bb64d --- /dev/null +++ b/docs/markdown/snippets/find_program.md @@ -0,0 +1,20 @@ +## find_program: Fixes when the program has been overridden by executable + +When a program has been overridden by an executable, the returned object of +find_program() had some issues: + +```meson +# In a subproject: +exe = executable('foo', ...) +meson.override_find_program('foo', exe) + +# In main project: +# The version check was crashing meson. +prog = find_program('foo', version : '>=1.0') + +# This was crashing meson. +message(prog.path()) + +# New method to be consistent with built objects. +message(prog.full_path()) +``` diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 48b6bd6..40f8b26 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -509,11 +509,14 @@ class DependencyHolder(InterpreterObject, ObjectHolder): return DependencyHolder(new_dep, self.subproject) class ExternalProgramHolder(InterpreterObject, ObjectHolder): - def __init__(self, ep): + def __init__(self, ep, subproject, backend=None): InterpreterObject.__init__(self) ObjectHolder.__init__(self, ep) + self.subproject = subproject + self.backend = backend self.methods.update({'found': self.found_method, - 'path': self.path_method}) + 'path': self.path_method, + 'full_path': self.full_path_method}) self.cached_version = None @noPosargs @@ -524,7 +527,20 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): @noPosargs @permittedKwargs({}) def path_method(self, args, kwargs): - return self.held_object.get_path() + mlog.deprecation('path() method is deprecated and replaced by full_path()') + return self._full_path() + + @noPosargs + @permittedKwargs({}) + @FeatureNew('ExternalProgram.full_path', '0.55.0') + def full_path_method(self, args, kwargs): + return self._full_path() + + def _full_path(self): + exe = self.held_object + if isinstance(exe, build.Executable): + return self.backend.get_target_filename_abs(exe) + return exe.get_path() def found(self): return isinstance(self.held_object, build.Executable) or self.held_object.found() @@ -533,9 +549,14 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): return self.held_object.get_command() def get_name(self): - return self.held_object.get_name() + exe = self.held_object + if isinstance(exe, build.Executable): + return exe.name + return exe.get_name() def get_version(self, interpreter): + if isinstance(self.held_object, build.Executable): + return self.held_object.project_version if not self.cached_version: raw_cmd = self.get_command() + ['--version'] cmd = [self, '--version'] @@ -2366,7 +2387,7 @@ class Interpreter(InterpreterBase): elif isinstance(item, dependencies.Dependency): return DependencyHolder(item, self.subproject) elif isinstance(item, dependencies.ExternalProgram): - return ExternalProgramHolder(item) + return ExternalProgramHolder(item, self.subproject) elif hasattr(item, 'held_object'): return item else: @@ -2389,7 +2410,7 @@ class Interpreter(InterpreterBase): elif isinstance(v, build.Data): self.build.data.append(v) elif isinstance(v, dependencies.ExternalProgram): - return ExternalProgramHolder(v) + return ExternalProgramHolder(v, self.subproject) elif isinstance(v, dependencies.InternalDependency): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps @@ -3143,7 +3164,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Executable name must be a string') prog = ExternalProgram.from_bin_list(self.environment, for_machine, p) if prog.found(): - return ExternalProgramHolder(prog) + return ExternalProgramHolder(prog, self.subproject) return None def program_from_system(self, args, search_dirs, silent=False): @@ -3170,7 +3191,7 @@ external dependencies (including libraries) must go to "dependencies".''') extprog = dependencies.ExternalProgram(exename, search_dir=search_dir, extra_search_dirs=extra_search_dirs, silent=silent) - progobj = ExternalProgramHolder(extprog) + progobj = ExternalProgramHolder(extprog, self.subproject) if progobj.found(): return progobj @@ -3183,7 +3204,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not silent: mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), '(overridden: %s)' % exe.description()) - return ExternalProgramHolder(exe) + return ExternalProgramHolder(exe, self.subproject, self.backend) return None def store_name_lookups(self, command_names): @@ -3214,11 +3235,11 @@ external dependencies (including libraries) must go to "dependencies".''') 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) + 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)) if progobj is None: - return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) # Only store successful lookups self.store_name_lookups(args) if wanted: @@ -3231,7 +3252,7 @@ external dependencies (including libraries) must go to "dependencies".''') 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))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) return progobj @FeatureNewKwargs('find_program', '0.53.0', ['dirs']) @@ -3246,7 +3267,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))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) search_dirs = extract_search_dirs(kwargs) wanted = mesonlib.stringlistify(kwargs.get('version', [])) @@ -3321,7 +3342,7 @@ external dependencies (including libraries) must go to "dependencies".''') return dep = subi.get_variable_method([varname], {}) if dep.held_object != cached_dep: - m = 'Inconsistency: Subproject has overriden the dependency with another variable than {!r}' + m = 'Inconsistency: Subproject has overridden the dependency with another variable than {!r}' raise DependencyException(m.format(varname)) def get_subproject_dep(self, name, display_name, dirname, varname, kwargs): @@ -3333,7 +3354,7 @@ external dependencies (including libraries) must go to "dependencies".''') subproject = self.subprojects[dirname] _, cached_dep = self._find_cached_dep(name, kwargs) if varname is None: - # Assuming the subproject overriden the dependency we want + # Assuming the subproject overridden the dependency we want if cached_dep: if required and not cached_dep.found(): m = 'Dependency {!r} is not satisfied' @@ -4551,6 +4572,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s kwargs['include_directories'] = self.extract_incdirs(kwargs) target = targetclass(name, self.subdir, self.subproject, for_machine, sources, objs, self.environment, kwargs) + target.project_version = self.project_version if not self.environment.machines.matches_build_machine(for_machine): self.add_cross_stdlib_info(target) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index a5c58a2..79e1824 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -285,7 +285,7 @@ print (json.dumps ({ class PythonInstallation(ExternalProgramHolder): def __init__(self, interpreter, python, info): - ExternalProgramHolder.__init__(self, python) + ExternalProgramHolder.__init__(self, python, interpreter.subproject) self.interpreter = interpreter self.subproject = self.interpreter.subproject prefix = self.interpreter.environment.coredata.get_builtin_option('prefix') @@ -514,7 +514,7 @@ class PythonModule(ExtensionModule): if disabled: mlog.log('Program', name_or_path or 'python', 'found:', mlog.red('NO'), '(disabled by:', mlog.bold(feature), ')') - return ExternalProgramHolder(NonExistingExternalProgram()) + return ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) if not name_or_path: python = ExternalProgram('python3', mesonlib.python_command, silent=True) @@ -561,11 +561,11 @@ class PythonModule(ExtensionModule): if not python.found(): if required: raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python')) - res = ExternalProgramHolder(NonExistingExternalProgram()) + res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) elif missing_modules: if required: raise mesonlib.MesonException('{} is missing modules: {}'.format(name_or_path or 'python', ', '.join(missing_modules))) - res = ExternalProgramHolder(NonExistingExternalProgram()) + res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) else: # Sanity check, we expect to have something that at least quacks in tune try: @@ -583,7 +583,7 @@ class PythonModule(ExtensionModule): if isinstance(info, dict) and 'version' in info and self._check_version(name_or_path, info['version']): res = PythonInstallation(interpreter, python, info) else: - res = ExternalProgramHolder(NonExistingExternalProgram()) + res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) if required: raise mesonlib.MesonException('{} is not a valid python or it is missing setuptools'.format(python)) diff --git a/test cases/common/201 override with exe/meson.build b/test cases/common/201 override with exe/meson.build index 81f6c02..62d2f32 100644 --- a/test cases/common/201 override with exe/meson.build +++ b/test cases/common/201 override with exe/meson.build @@ -1,6 +1,10 @@ project('myexe', 'c') sub = subproject('sub') -prog = find_program('foobar') + +prog = find_program('foobar', version : '>= 2.0', required : false) +assert(not prog.found()) + +prog = find_program('foobar', version : '>= 1.0') custom1 = custom_target('custom1', build_by_default : true, input : [], @@ -11,5 +15,7 @@ gen = generator(prog, arguments : ['@OUTPUT@']) custom2 = gen.process('main2.input') +message(prog.full_path()) + executable('e1', custom1) executable('e2', custom2) diff --git a/test cases/common/201 override with exe/subprojects/sub/meson.build b/test cases/common/201 override with exe/subprojects/sub/meson.build index 1f186da..f0343b2 100644 --- a/test cases/common/201 override with exe/subprojects/sub/meson.build +++ b/test cases/common/201 override with exe/subprojects/sub/meson.build @@ -1,3 +1,3 @@ -project('sub', 'c') +project('sub', 'c', version : '1.0') foobar = executable('foobar', 'foobar.c', native : true) meson.override_find_program('foobar', foobar) |