From 5a7b8d86d0c455680096d47cb87f0de08c0954ac Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sat, 21 Aug 2021 23:27:29 -0400 Subject: [PATCH] use a more informative error message for invoking meson in a subdir Explicitly mention that the project definition is invalid, and clarify that project is `project()` -- a function. Also try to walk the directory tree upward, and if there are parent meson.build files, just say this isn't the project root, and "maybe you meant to run meson there instead?" This won't catch calls to subdir('foo/bar') but we can't be perfect, only better than before and catch the *majority* of such cases, and hopefully it's a lot more clear if meson protests that the project is "invalid, there is no project() function", where the user should look for a potential solution. Fixes #3426 --- mesonbuild/interpreterbase/interpreterbase.py | 16 ++++++++++++++-- test cases/failing/1 project not first/test.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index f0668e417..4930bee24 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -45,7 +45,7 @@ from .disabler import Disabler, is_disabled from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders from ._unholder import _unholder -import os, copy, re +import os, copy, re, pathlib import typing as T if T.TYPE_CHECKING: @@ -123,7 +123,19 @@ class InterpreterBase: raise InvalidCode('No statements in code.') first = self.ast.lines[0] if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project': - raise InvalidCode('First statement must be a call to project') + p = pathlib.Path(self.source_root).resolve() + found = p + for parent in p.parents: + if (parent / 'meson.build').is_file(): + found = parent + else: + break + + error = 'first statement must be a call to project()' + if found != p: + raise InvalidCode(f'Not the project root: {error}\n\nDid you mean to run meson from the directory: "{found}"?') + else: + raise InvalidCode(f'Invalid source tree: {error}') def run(self) -> None: # Evaluate everything after the first line, which is project() because diff --git a/test cases/failing/1 project not first/test.json b/test cases/failing/1 project not first/test.json index 70f3c41ac..27bae0239 100644 --- a/test cases/failing/1 project not first/test.json +++ b/test cases/failing/1 project not first/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "ERROR: First statement must be a call to project" + "line": "ERROR: Invalid source tree: first statement must be a call to project()" } ] }