diff options
author | Eli Schwartz <eschwartz@archlinux.org> | 2023-02-26 01:36:47 -0500 |
---|---|---|
committer | Eli Schwartz <eschwartz@archlinux.org> | 2023-03-01 23:30:49 -0500 |
commit | 878c1604e6348f98602bbdd16d92fc63ed15ebea (patch) | |
tree | b2d351ce888291cacb11dcce34178e69fd4c8891 /mesonbuild/interpreter/interpreter.py | |
parent | cc23996266846890fea708a7eb5dcff66d44c8b6 (diff) | |
download | meson-878c1604e6348f98602bbdd16d92fc63ed15ebea.zip meson-878c1604e6348f98602bbdd16d92fc63ed15ebea.tar.gz meson-878c1604e6348f98602bbdd16d92fc63ed15ebea.tar.bz2 |
handle meson_version even when the build file fails to parse
If the meson.build file is sufficiently "broken", even attempting to lex
and parse it will totally fail, and we error out without getting the
opportunity to evalaute the project() function. This can fairly easily
happen if we add new grammar to the syntax, which old versions of meson
cannot understand. Setting a minimum meson_version doesn't help, because
people with a too-old version of meson get parser errors instead of
advice about upgrading meson.
Examples of this include adding dict support to meson.
There are two general approaches to solving this issue, one of which
projects are empowered to do:
- refactor the project to place too-new syntax in a subdir() loaded
build file, so the root file can be interpreted
- teach meson to catch errors in building the initial AST, and just load
enough of the AST to check for meson_version advice
This implements the latter, allowing to future-proof the build
grammar.
Diffstat (limited to 'mesonbuild/interpreter/interpreter.py')
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9e20446..c6602a5 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -525,6 +525,24 @@ class Interpreter(InterpreterBase, HoldableObject): else: raise InterpreterException(f'Module returned a value of unknown type {v!r}.') + def handle_meson_version(self, pv: str, location: mparser.BaseNode) -> None: + if not mesonlib.version_compare(coredata.version, pv): + raise InterpreterException.from_node(f'Meson version is {coredata.version} but project requires {pv}', node=location) + + def handle_meson_version_from_ast(self) -> None: + if not self.ast.lines: + return + project = self.ast.lines[0] + # first line is always project() + if not isinstance(project, mparser.FunctionNode): + return + for kw, val in project.args.kwargs.items(): + assert isinstance(kw, mparser.IdNode), 'for mypy' + if kw.value == 'meson_version': + # mypy does not understand "and isinstance" + if isinstance(val, mparser.StringNode): + self.handle_meson_version(val.value, val) + def get_build_def_files(self) -> mesonlib.OrderedSet[str]: return self.build_def_files @@ -1151,10 +1169,7 @@ class Interpreter(InterpreterBase, HoldableObject): # This needs to be evaluated as early as possible, as meson uses this # for things like deprecation testing. if kwargs['meson_version']: - cv = coredata.version - pv = kwargs['meson_version'] - if not mesonlib.version_compare(cv, pv): - raise InterpreterException(f'Meson version is {cv} but project requires {pv}') + self.handle_meson_version(kwargs['meson_version'], node) mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] if os.path.exists(self.option_file): |