aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2020-04-20 16:23:33 -0400
committerJussi Pakkanen <jpakkane@gmail.com>2020-04-28 01:39:56 +0300
commit39a69d1fb0e130fae9f64b81e0a992503869a97a (patch)
tree2f9cbed234a6d55ba033acda49ec438e7c34fa3a
parent34e7e8780c0196313be8700f504ec84fd6cba3d1 (diff)
downloadmeson-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.md10
-rw-r--r--docs/markdown/snippets/find_program.md20
-rw-r--r--mesonbuild/interpreter.py52
-rw-r--r--mesonbuild/modules/python.py10
-rw-r--r--test cases/common/201 override with exe/meson.build8
-rw-r--r--test cases/common/201 override with exe/subprojects/sub/meson.build2
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)