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
pull/9148/head
Eli Schwartz 3 years ago committed by Jussi Pakkanen
parent db04a3f5f2
commit 5a7b8d86d0
  1. 16
      mesonbuild/interpreterbase/interpreterbase.py
  2. 2
      test cases/failing/1 project not first/test.json

@ -45,7 +45,7 @@ from .disabler import Disabler, is_disabled
from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders
from ._unholder import _unholder from ._unholder import _unholder
import os, copy, re import os, copy, re, pathlib
import typing as T import typing as T
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
@ -123,7 +123,19 @@ class InterpreterBase:
raise InvalidCode('No statements in code.') raise InvalidCode('No statements in code.')
first = self.ast.lines[0] first = self.ast.lines[0]
if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project': 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: def run(self) -> None:
# Evaluate everything after the first line, which is project() because # Evaluate everything after the first line, which is project() because

@ -1,7 +1,7 @@
{ {
"stdout": [ "stdout": [
{ {
"line": "ERROR: First statement must be a call to project" "line": "ERROR: Invalid source tree: first statement must be a call to project()"
} }
] ]
} }

Loading…
Cancel
Save