aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2020-03-01 18:17:41 +0200
committerGitHub <noreply@github.com>2020-03-01 18:17:41 +0200
commit25cbcb19a9208ebf8f5cde3f8ecb91df0a2dfebf (patch)
treecf29f0c60ac4014b9395edbe2a9c3efdad449291
parent74452f2a1c842291c893504876507946103ac77f (diff)
parent96f661e15046a4222fd01a7216e18de901b73cb6 (diff)
downloadmeson-25cbcb19a9208ebf8f5cde3f8ecb91df0a2dfebf.zip
meson-25cbcb19a9208ebf8f5cde3f8ecb91df0a2dfebf.tar.gz
meson-25cbcb19a9208ebf8f5cde3f8ecb91df0a2dfebf.tar.bz2
Merge pull request #6627 from jon-turney/cwd-relative-file-locations
Consistently report file locations relative to cwd
-rw-r--r--docs/markdown/snippets/consistent_file_locations.md9
-rw-r--r--mesonbuild/ast/interpreter.py4
-rw-r--r--mesonbuild/interpreter.py4
-rw-r--r--mesonbuild/interpreterbase.py4
-rw-r--r--mesonbuild/mesonlib.py17
-rw-r--r--mesonbuild/mlog.py16
-rw-r--r--mesonbuild/mparser.py70
-rw-r--r--mesonbuild/optinterpreter.py7
-rw-r--r--mesonbuild/rewriter.py35
-rwxr-xr-xrun_unittests.py51
-rw-r--r--test cases/failing/100 subdir parse error/meson.build2
-rw-r--r--test cases/failing/100 subdir parse error/subdir/meson.build1
-rw-r--r--test cases/failing/101 invalid option file/meson.build1
-rw-r--r--test cases/failing/101 invalid option file/meson_options.txt1
14 files changed, 137 insertions, 85 deletions
diff --git a/docs/markdown/snippets/consistent_file_locations.md b/docs/markdown/snippets/consistent_file_locations.md
new file mode 100644
index 0000000..5f384fe
--- /dev/null
+++ b/docs/markdown/snippets/consistent_file_locations.md
@@ -0,0 +1,9 @@
+## Consistently report file locations relative to cwd
+
+The paths for filenames in error and warning locations are now consistently
+reported relative to the current working directory (when possible), or as
+absolute paths (when a relative path does not exist, e.g. a Windows path
+starting with a different drive letter to the current working directory).
+
+(The previous behaviour was to report a path relative to the source root for all
+warnings and most errors, and relative to cwd for certain parser errors)
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index b5aade3..5f47ad3 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -154,9 +154,9 @@ class AstInterpreter(interpreterbase.InterpreterBase):
code = f.read()
assert(isinstance(code, str))
try:
- codeblock = mparser.Parser(code, subdir).parse()
+ codeblock = mparser.Parser(code, absname).parse()
except mesonlib.MesonException as me:
- me.file = buildfilename
+ me.file = absname
raise me
self.subdir = subdir
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index c29ed89..a9e0a3b 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -3748,9 +3748,9 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
code = f.read()
assert(isinstance(code, str))
try:
- codeblock = mparser.Parser(code, self.subdir).parse()
+ codeblock = mparser.Parser(code, absname).parse()
except mesonlib.MesonException as me:
- me.file = buildfilename
+ me.file = absname
raise me
try:
self.evaluate_codeblock(codeblock)
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 2a976d3..d723da5 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -381,7 +381,7 @@ class InterpreterBase:
raise InvalidCode('Builder file is empty.')
assert(isinstance(code, str))
try:
- self.ast = mparser.Parser(code, self.subdir).parse()
+ self.ast = mparser.Parser(code, mesonfile).parse()
except mesonlib.MesonException as me:
me.file = mesonfile
raise me
@@ -432,7 +432,7 @@ class InterpreterBase:
if not hasattr(e, 'lineno'):
e.lineno = cur.lineno
e.colno = cur.colno
- e.file = os.path.join(self.subdir, 'meson.build')
+ e.file = os.path.join(self.source_root, self.subdir, environment.build_filename)
raise e
i += 1 # In THE FUTURE jump over blocks and stuff.
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index e09d123..854c934 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -122,13 +122,6 @@ an_unpicklable_object = threading.Lock()
class MesonException(Exception):
'''Exceptions thrown by Meson'''
- def get_msg_with_context(self):
- s = ''
- if hasattr(self, 'lineno') and hasattr(self, 'file'):
- s = get_error_location_string(self.file, self.lineno) + ' '
- s += str(self)
- return s
-
class EnvironmentException(MesonException):
'''Exceptions thrown while processing and creating the build environment'''
@@ -1339,16 +1332,6 @@ def detect_subprojects(spdir_name, current_dir='', result=None):
result[basename] = [trial]
return result
-# This isn't strictly correct. What we really want here is something like:
-# class StringProtocol(typing_extensions.Protocol):
-#
-# def __str__(self) -> str: ...
-#
-# This would more accurately embody what this funcitonc an handle, but we
-# don't have that yet, so instead we'll do some casting to work around it
-def get_error_location_string(fname: str, lineno: str) -> str:
- return '{}:{}:'.format(fname, lineno)
-
def substring_is_in_list(substr: str, strlist: T.List[str]) -> bool:
for s in strlist:
if substr in s:
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index 64c16f5..a1acfa3 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -221,11 +221,19 @@ def log_once(*args: T.Union[str, AnsiDecorator], is_error: bool = False,
_logged_once.add(t)
log(*args, is_error=is_error, **kwargs)
+# This isn't strictly correct. What we really want here is something like:
+# class StringProtocol(typing_extensions.Protocol):
+#
+# def __str__(self) -> str: ...
+#
+# This would more accurately embody what this function can handle, but we
+# don't have that yet, so instead we'll do some casting to work around it
+def get_error_location_string(fname: str, lineno: str) -> str:
+ return '{}:{}:'.format(fname, lineno)
+
def _log_error(severity: str, *rargs: T.Union[str, AnsiDecorator],
once: bool = False, **kwargs: T.Any) -> None:
- from .mesonlib import get_error_location_string
- from .environment import build_filename
- from .mesonlib import MesonException
+ from .mesonlib import MesonException, relpath
# The typing requirements here are non-obvious. Lists are invariant,
# therefore T.List[A] and T.List[T.Union[A, B]] are not able to be joined
@@ -242,7 +250,7 @@ def _log_error(severity: str, *rargs: T.Union[str, AnsiDecorator],
location = kwargs.pop('location', None)
if location is not None:
- location_file = os.path.join(location.subdir, build_filename)
+ location_file = relpath(location.filename, os.getcwd())
location_str = get_error_location_string(location_file, location.lineno)
# Unions are frankly awful, and we have to T.cast here to get mypy
# to understand that the list concatenation is safe
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 2b503f1..430c89e 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -72,9 +72,9 @@ class BlockParseException(MesonException):
self.colno = colno
class Token:
- def __init__(self, tid, subdir, line_start, lineno, colno, bytespan, value):
+ def __init__(self, tid, filename, line_start, lineno, colno, bytespan, value):
self.tid = tid
- self.subdir = subdir
+ self.filename = filename
self.line_start = line_start
self.lineno = lineno
self.colno = colno
@@ -132,7 +132,7 @@ class Lexer:
def getline(self, line_start):
return self.code[line_start:self.code.find('\n', line_start)]
- def lex(self, subdir):
+ def lex(self, filename):
line_start = 0
lineno = 1
loc = 0
@@ -205,9 +205,9 @@ This will become a hard error in a future Meson release.""", self.getline(line_s
else:
if match_text in self.future_keywords:
mlog.warning("Identifier '{}' will become a reserved keyword in a future release. Please rename it.".format(match_text),
- location=types.SimpleNamespace(subdir=subdir, lineno=lineno))
+ location=types.SimpleNamespace(filename=filename, lineno=lineno))
value = match_text
- yield Token(tid, subdir, curline_start, curline, col, bytespan, value)
+ yield Token(tid, filename, curline_start, curline, col, bytespan, value)
break
if not matched:
raise ParseException('lexer', self.getline(line_start), lineno, col)
@@ -223,7 +223,7 @@ class BaseNode:
class ElementaryNode(BaseNode):
def __init__(self, token):
self.lineno = token.lineno
- self.subdir = token.subdir
+ self.filename = token.filename
self.colno = token.colno
self.value = token.value
self.bytespan = token.bytespan
@@ -263,7 +263,7 @@ class BreakNode(ElementaryNode):
class ArrayNode(BaseNode):
def __init__(self, args, lineno, colno, end_lineno, end_colno):
- self.subdir = args.subdir
+ self.filename = args.filename
self.lineno = lineno
self.colno = colno
self.end_lineno = end_lineno
@@ -272,7 +272,7 @@ class ArrayNode(BaseNode):
class DictNode(BaseNode):
def __init__(self, args, lineno, colno, end_lineno, end_colno):
- self.subdir = args.subdir
+ self.filename = args.filename
self.lineno = lineno
self.colno = colno
self.end_lineno = end_lineno
@@ -281,14 +281,14 @@ class DictNode(BaseNode):
class EmptyNode(BaseNode):
def __init__(self, lineno, colno):
- self.subdir = ''
+ self.filename = ''
self.lineno = lineno
self.colno = colno
self.value = None
class OrNode(BaseNode):
def __init__(self, left, right):
- self.subdir = left.subdir
+ self.filename = left.filename
self.lineno = left.lineno
self.colno = left.colno
self.left = left
@@ -296,7 +296,7 @@ class OrNode(BaseNode):
class AndNode(BaseNode):
def __init__(self, left, right):
- self.subdir = left.subdir
+ self.filename = left.filename
self.lineno = left.lineno
self.colno = left.colno
self.left = left
@@ -306,14 +306,14 @@ class ComparisonNode(BaseNode):
def __init__(self, ctype, left, right):
self.lineno = left.lineno
self.colno = left.colno
- self.subdir = left.subdir
+ self.filename = left.filename
self.left = left
self.right = right
self.ctype = ctype
class ArithmeticNode(BaseNode):
def __init__(self, operation, left, right):
- self.subdir = left.subdir
+ self.filename = left.filename
self.lineno = left.lineno
self.colno = left.colno
self.left = left
@@ -322,14 +322,14 @@ class ArithmeticNode(BaseNode):
class NotNode(BaseNode):
def __init__(self, location_node, value):
- self.subdir = location_node.subdir
+ self.filename = location_node.filename
self.lineno = location_node.lineno
self.colno = location_node.colno
self.value = value
class CodeBlockNode(BaseNode):
def __init__(self, location_node):
- self.subdir = location_node.subdir
+ self.filename = location_node.filename
self.lineno = location_node.lineno
self.colno = location_node.colno
self.lines = []
@@ -338,13 +338,13 @@ class IndexNode(BaseNode):
def __init__(self, iobject, index):
self.iobject = iobject
self.index = index
- self.subdir = iobject.subdir
+ self.filename = iobject.filename
self.lineno = iobject.lineno
self.colno = iobject.colno
class MethodNode(BaseNode):
- def __init__(self, subdir, lineno, colno, source_object, name, args):
- self.subdir = subdir
+ def __init__(self, filename, lineno, colno, source_object, name, args):
+ self.filename = filename
self.lineno = lineno
self.colno = colno
self.source_object = source_object
@@ -353,8 +353,8 @@ class MethodNode(BaseNode):
self.args = args
class FunctionNode(BaseNode):
- def __init__(self, subdir, lineno, colno, end_lineno, end_colno, func_name, args):
- self.subdir = subdir
+ def __init__(self, filename, lineno, colno, end_lineno, end_colno, func_name, args):
+ self.filename = filename
self.lineno = lineno
self.colno = colno
self.end_lineno = end_lineno
@@ -364,8 +364,8 @@ class FunctionNode(BaseNode):
self.args = args
class AssignmentNode(BaseNode):
- def __init__(self, subdir, lineno, colno, var_name, value):
- self.subdir = subdir
+ def __init__(self, filename, lineno, colno, var_name, value):
+ self.filename = filename
self.lineno = lineno
self.colno = colno
self.var_name = var_name
@@ -373,8 +373,8 @@ class AssignmentNode(BaseNode):
self.value = value
class PlusAssignmentNode(BaseNode):
- def __init__(self, subdir, lineno, colno, var_name, value):
- self.subdir = subdir
+ def __init__(self, filename, lineno, colno, var_name, value):
+ self.filename = filename
self.lineno = lineno
self.colno = colno
self.var_name = var_name
@@ -398,7 +398,7 @@ class IfClauseNode(BaseNode):
class UMinusNode(BaseNode):
def __init__(self, current_location, value):
- self.subdir = current_location.subdir
+ self.filename = current_location.filename
self.lineno = current_location.lineno
self.colno = current_location.colno
self.value = value
@@ -411,8 +411,8 @@ class IfNode(BaseNode):
self.block = block
class TernaryNode(BaseNode):
- def __init__(self, subdir, lineno, colno, condition, trueblock, falseblock):
- self.subdir = subdir
+ def __init__(self, filename, lineno, colno, condition, trueblock, falseblock):
+ self.filename = filename
self.lineno = lineno
self.colno = colno
self.condition = condition
@@ -423,7 +423,7 @@ class ArgumentNode(BaseNode):
def __init__(self, token):
self.lineno = token.lineno
self.colno = token.colno
- self.subdir = token.subdir
+ self.filename = token.filename
self.arguments = []
self.commas = []
self.kwargs = {}
@@ -485,9 +485,9 @@ comparison_map = {'equal': '==',
# 9 plain token
class Parser:
- def __init__(self, code, subdir):
+ def __init__(self, code, filename):
self.lexer = Lexer(code)
- self.stream = self.lexer.lex(subdir)
+ self.stream = self.lexer.lex(filename)
self.current = Token('eof', '', 0, 0, 0, (0, 0), None)
self.getsym()
self.in_ternary = False
@@ -531,13 +531,13 @@ class Parser:
value = self.e1()
if not isinstance(left, IdNode):
raise ParseException('Plusassignment target must be an id.', self.getline(), left.lineno, left.colno)
- return PlusAssignmentNode(left.subdir, left.lineno, left.colno, left.value, value)
+ return PlusAssignmentNode(left.filename, left.lineno, left.colno, left.value, value)
elif self.accept('assign'):
value = self.e1()
if not isinstance(left, IdNode):
raise ParseException('Assignment target must be an id.',
self.getline(), left.lineno, left.colno)
- return AssignmentNode(left.subdir, left.lineno, left.colno, left.value, value)
+ return AssignmentNode(left.filename, left.lineno, left.colno, left.value, value)
elif self.accept('questionmark'):
if self.in_ternary:
raise ParseException('Nested ternary operators are not allowed.',
@@ -547,7 +547,7 @@ class Parser:
self.expect('colon')
falseblock = self.e1()
self.in_ternary = False
- return TernaryNode(left.subdir, left.lineno, left.colno, left, trueblock, falseblock)
+ return TernaryNode(left.filename, left.lineno, left.colno, left, trueblock, falseblock)
return left
def e2(self):
@@ -626,7 +626,7 @@ class Parser:
if not isinstance(left, IdNode):
raise ParseException('Function call must be applied to plain id',
self.getline(), left.lineno, left.colno)
- left = FunctionNode(left.subdir, left.lineno, left.colno, self.current.lineno, self.current.colno, left.value, args)
+ left = FunctionNode(left.filename, left.lineno, left.colno, self.current.lineno, self.current.colno, left.value, args)
go_again = True
while go_again:
go_again = False
@@ -718,7 +718,7 @@ class Parser:
self.expect('lparen')
args = self.args()
self.expect('rparen')
- method = MethodNode(methodname.subdir, methodname.lineno, methodname.colno, source_object, methodname.value, args)
+ method = MethodNode(methodname.filename, methodname.lineno, methodname.colno, source_object, methodname.value, args)
if self.accept('dot'):
return self.method_call(method)
return method
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 1a8a04a..fd0a8c9 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os, re
+import re
import functools
import typing as T
@@ -140,13 +140,14 @@ class OptionInterpreter:
def process(self, option_file):
try:
with open(option_file, 'r', encoding='utf8') as f:
- ast = mparser.Parser(f.read(), '').parse()
+ ast = mparser.Parser(f.read(), option_file).parse()
except mesonlib.MesonException as me:
me.file = option_file
raise me
if not isinstance(ast, mparser.CodeBlockNode):
e = OptionException('Option file is malformed.')
e.lineno = ast.lineno()
+ e.file = option_file
raise e
for cur in ast.lines:
try:
@@ -154,7 +155,7 @@ class OptionInterpreter:
except Exception as e:
e.lineno = cur.lineno
e.colno = cur.colno
- e.file = os.path.join('meson_options.txt')
+ e.file = option_file
raise e
def reduce_single(self, arg):
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index b634d00..39d8337 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -651,8 +651,8 @@ class Rewriter:
mlog.log(' -- Source', mlog.green(i), 'is already defined for the target --> skipping')
continue
mlog.log(' -- Adding source', mlog.green(i), 'at',
- mlog.yellow('{}:{}'.format(os.path.join(node.subdir, environment.build_filename), node.lineno)))
- token = Token('string', node.subdir, 0, 0, 0, None, i)
+ mlog.yellow('{}:{}'.format(node.filename, node.lineno)))
+ token = Token('string', node.filename, 0, 0, 0, None, i)
to_append += [StringNode(token)]
# Append to the AST at the right place
@@ -695,7 +695,7 @@ class Rewriter:
arg_node = root
assert(arg_node is not None)
mlog.log(' -- Removing source', mlog.green(i), 'from',
- mlog.yellow('{}:{}'.format(os.path.join(string_node.subdir, environment.build_filename), string_node.lineno)))
+ mlog.yellow('{}:{}'.format(string_node.filename, string_node.lineno)))
arg_node.arguments.remove(string_node)
# Mark the node as modified
@@ -712,23 +712,24 @@ class Rewriter:
id_base = re.sub(r'[- ]', '_', cmd['target'])
target_id = id_base + '_exe' if cmd['target_type'] == 'executable' else '_lib'
source_id = id_base + '_sources'
+ filename = os.path.join(cmd['subdir'], environment.build_filename)
# Build src list
- src_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
+ src_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
src_arr_node = ArrayNode(src_arg_node, 0, 0, 0, 0)
- src_far_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
- src_fun_node = FunctionNode(cmd['subdir'], 0, 0, 0, 0, 'files', src_far_node)
- src_ass_node = AssignmentNode(cmd['subdir'], 0, 0, source_id, src_fun_node)
- src_arg_node.arguments = [StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, x)) for x in cmd['sources']]
+ src_far_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
+ src_fun_node = FunctionNode(filename, 0, 0, 0, 0, 'files', src_far_node)
+ src_ass_node = AssignmentNode(filename, 0, 0, source_id, src_fun_node)
+ src_arg_node.arguments = [StringNode(Token('string', filename, 0, 0, 0, None, x)) for x in cmd['sources']]
src_far_node.arguments = [src_arr_node]
# Build target
- tgt_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
- tgt_fun_node = FunctionNode(cmd['subdir'], 0, 0, 0, 0, cmd['target_type'], tgt_arg_node)
- tgt_ass_node = AssignmentNode(cmd['subdir'], 0, 0, target_id, tgt_fun_node)
+ tgt_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
+ tgt_fun_node = FunctionNode(filename, 0, 0, 0, 0, cmd['target_type'], tgt_arg_node)
+ tgt_ass_node = AssignmentNode(filename, 0, 0, target_id, tgt_fun_node)
tgt_arg_node.arguments = [
- StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, cmd['target'])),
- IdNode(Token('string', cmd['subdir'], 0, 0, 0, None, source_id))
+ StringNode(Token('string', filename, 0, 0, 0, None, cmd['target'])),
+ IdNode(Token('string', filename, 0, 0, 0, None, source_id))
]
src_ass_node.accept(AstIndentationGenerator())
@@ -741,7 +742,7 @@ class Rewriter:
to_remove = target['node']
self.to_remove_nodes += [to_remove]
mlog.log(' -- Removing target', mlog.green(cmd['target']), 'at',
- mlog.yellow('{}:{}'.format(os.path.join(to_remove.subdir, environment.build_filename), to_remove.lineno)))
+ mlog.yellow('{}:{}'.format(to_remove.filename, to_remove.lineno)))
elif cmd['operation'] == 'info':
# T.List all sources in the target
@@ -776,8 +777,8 @@ class Rewriter:
self.functions[cmd['type']](cmd)
def apply_changes(self):
- assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'subdir') for x in self.modefied_nodes))
- assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'subdir') for x in self.to_remove_nodes))
+ assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'filename') for x in self.modefied_nodes))
+ assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'filename') for x in self.to_remove_nodes))
assert(all(isinstance(x, (ArrayNode, FunctionNode)) for x in self.modefied_nodes))
assert(all(isinstance(x, (ArrayNode, AssignmentNode, FunctionNode)) for x in self.to_remove_nodes))
# Sort based on line and column in reversed order
@@ -796,7 +797,7 @@ class Rewriter:
printer.post_process()
new_data = printer.result.strip()
data = {
- 'file': os.path.join(i['node'].subdir, environment.build_filename),
+ 'file': i['node'].filename,
'str': new_data,
'node': i['node'],
'action': i['action']
diff --git a/run_unittests.py b/run_unittests.py
index 0861f59..d091cbc 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -56,7 +56,7 @@ from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,
is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
windows_proof_rmtree, python_command, version_compare, split_args,
- quote_arg
+ quote_arg, relpath
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException
@@ -1512,7 +1512,8 @@ class BasePlatformTests(unittest.TestCase):
extra_args=None,
default_args=True,
inprocess=False,
- override_envvars=None):
+ override_envvars=None,
+ workdir=None):
self.assertPathExists(srcdir)
if extra_args is None:
extra_args = []
@@ -1553,7 +1554,7 @@ class BasePlatformTests(unittest.TestCase):
mesonbuild.mlog.log_file = None
else:
try:
- out = self._run(self.setup_command + args + extra_args, override_envvars=override_envvars)
+ out = self._run(self.setup_command + args + extra_args, override_envvars=override_envvars, workdir=workdir)
except unittest.SkipTest:
raise unittest.SkipTest('Project requested skipping: ' + srcdir)
except Exception:
@@ -3171,6 +3172,50 @@ int main(int argc, char **argv) {
]:
self.assertRegex(out, re.escape(expected))
+ for wd in [
+ self.src_root,
+ self.builddir,
+ os.getcwd(),
+ ]:
+ self.new_builddir()
+ out = self.init(tdir, workdir=wd)
+ expected = os.path.join(relpath(tdir, self.src_root), 'meson.build')
+ relwd = relpath(self.src_root, wd)
+ if relwd != '.':
+ expected = os.path.join(relwd, expected)
+ expected = '\n' + expected + ':'
+ self.assertIn(expected, out)
+
+ def test_error_location_path(self):
+ '''Test locations in meson errors contain correct paths'''
+ # this list contains errors from all the different steps in the
+ # lexer/parser/interpreter we have tests for.
+ for (t, f) in [
+ ('10 out of bounds', 'meson.build'),
+ ('18 wrong plusassign', 'meson.build'),
+ ('61 bad option argument', 'meson_options.txt'),
+ ('100 subdir parse error', os.path.join('subdir', 'meson.build')),
+ ('101 invalid option file', 'meson_options.txt'),
+ ]:
+ tdir = os.path.join(self.src_root, 'test cases', 'failing', t)
+
+ for wd in [
+ self.src_root,
+ self.builddir,
+ os.getcwd(),
+ ]:
+ try:
+ self.init(tdir, workdir=wd)
+ except subprocess.CalledProcessError as e:
+ expected = os.path.join('test cases', 'failing', t, f)
+ relwd = relpath(self.src_root, wd)
+ if relwd != '.':
+ expected = os.path.join(relwd, expected)
+ expected = '\n' + expected + ':'
+ self.assertIn(expected, e.output)
+ else:
+ self.fail('configure unexpectedly succeeded')
+
def test_permitted_method_kwargs(self):
tdir = os.path.join(self.unit_test_dir, '25 non-permitted kwargs')
out = self.init(tdir)
diff --git a/test cases/failing/100 subdir parse error/meson.build b/test cases/failing/100 subdir parse error/meson.build
new file mode 100644
index 0000000..ee5bb0c
--- /dev/null
+++ b/test cases/failing/100 subdir parse error/meson.build
@@ -0,0 +1,2 @@
+project('subdir false plusassign', 'c')
+subdir('subdir')
diff --git a/test cases/failing/100 subdir parse error/subdir/meson.build b/test cases/failing/100 subdir parse error/subdir/meson.build
new file mode 100644
index 0000000..3ac5ef9
--- /dev/null
+++ b/test cases/failing/100 subdir parse error/subdir/meson.build
@@ -0,0 +1 @@
+3 += 4
diff --git a/test cases/failing/101 invalid option file/meson.build b/test cases/failing/101 invalid option file/meson.build
new file mode 100644
index 0000000..b0347c3
--- /dev/null
+++ b/test cases/failing/101 invalid option file/meson.build
@@ -0,0 +1 @@
+project('invalid option file')
diff --git a/test cases/failing/101 invalid option file/meson_options.txt b/test cases/failing/101 invalid option file/meson_options.txt
new file mode 100644
index 0000000..eef843b
--- /dev/null
+++ b/test cases/failing/101 invalid option file/meson_options.txt
@@ -0,0 +1 @@
+'