Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions mesonbuild/interpreterbase/interpreterbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,15 +309,20 @@ def evaluate_if(self, node: mparser.IfClauseNode) -> T.Optional[Disabler]:
res = result.operator_call(MesonOperator.BOOL, None)
if not isinstance(res, bool):
raise InvalidCode(f'If clause {result!r} does not evaluate to true or false.')
if res:
prev_meson_version = mesonlib.project_meson_versions[self.subproject]
if self.tmp_meson_version:
mesonlib.project_meson_versions[self.subproject] = self.tmp_meson_version
try:
prev_meson_version = mesonlib.project_meson_versions[self.subproject]
if self.tmp_meson_version:
always = mesonlib.version_compare_conditions(prev_meson_version,
self.tmp_meson_version)
if always is not None:
mlog.warning(f"Version comparison '{self.tmp_meson_version}' always evaluates to {str(always).lower()}",
location=self.current_node)
mesonlib.project_meson_versions[self.subproject] = self.tmp_meson_version
try:
if res:
self.evaluate_codeblock(i.block)
finally:
mesonlib.project_meson_versions[self.subproject] = prev_meson_version
return None
return None
finally:
mesonlib.project_meson_versions[self.subproject] = prev_meson_version
if not isinstance(node.elseblock, mparser.EmptyNode):
self.evaluate_codeblock(node.elseblock.block)
return None
Expand Down
47 changes: 47 additions & 0 deletions mesonbuild/utils/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class _VerPickleLoadable(Protocol):
'unique_list',
'verbose_git',
'version_compare',
'version_compare_conditions',
'version_compare_condition_with_min',
'version_compare_many',
'search_version',
Expand Down Expand Up @@ -991,6 +992,52 @@ def version_compare_condition_with_min(condition: str, minimum: str) -> bool:

return T.cast('bool', cmpop(Version(minimum), Version(condition)))


# given the Meson version condition |outer_cond|, determine if the version
# condition |inner_cond| is always true or always false
def version_compare_conditions(outer_cond: str, inner_cond: str) -> T.Optional[bool]:
class Extracted:
def __init__(self, v: str):
self.op, val = _version_extract_cmpop(v)
val += '.0' * max(0, 2 - val.count('.'))
self.val = Version(val)
if self.op == operator.ne:
self.direction: T.Callable[[T.Any, T.Any], bool] = operator.eq
elif self.op == operator.le:
self.direction = operator.lt
elif self.op == operator.ge:
self.direction = operator.gt
else:
self.direction = self.op
self.inclusive = self.op in (operator.eq, operator.le, operator.ge)

inner, outer = Extracted(inner_cond), Extracted(outer_cond)
if inner.val == outer.val:
if outer.op == inner.op:
return True
if outer.direction == operator.eq:
if outer.inclusive and inner.inclusive:
return True
if outer.inclusive and not inner.inclusive:
return False
if not outer.inclusive and inner.op == operator.eq:
return False
elif outer.inclusive:
if not inner.inclusive and inner.direction not in (operator.eq, outer.direction):
return False
elif outer.direction != inner.direction:
return inner.op == operator.ne
else:
return True
if inner.val < outer.val:
if outer.op == operator.eq or outer.direction == operator.gt:
return inner.direction == operator.gt or inner.op == operator.ne
if inner.val > outer.val:
if outer.op == operator.eq or outer.direction == operator.lt:
return inner.direction == operator.lt or inner.op == operator.ne
return None


def search_version(text: str) -> str:
# Usually of the type 4.1.4 but compiler output may contain
# stuff like this:
Expand Down
7 changes: 7 additions & 0 deletions test cases/common/286 redundant version check/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
project('t', 'c', meson_version: '>=0.60.0')
if meson.version().version_compare('>=0.55.0')
v = 1
endif
if meson.version().version_compare('<0.60.0')
v = 2
endif
10 changes: 10 additions & 0 deletions test cases/common/286 redundant version check/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"stdout": [
{
"line": "test cases/common/286 redundant version check/meson.build:2: WARNING: Version comparison '>=0.55.0' always evaluates to true"
},
{
"line": "test cases/common/286 redundant version check/meson.build:5: WARNING: Version comparison '<0.60.0' always evaluates to false"
}
]
}
77 changes: 77 additions & 0 deletions unittests/internaltests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import argparse
import contextlib
import io
import itertools
import json
import operator
import os
Expand Down Expand Up @@ -830,6 +831,82 @@ def test_version_compare(self):
for o, name in [(operator.lt, 'lt'), (operator.le, 'le'), (operator.eq, 'eq')]:
self.assertFalse(o(ver_a, ver_b), f'{ver_a} {name} {ver_b}')

def test_version_compare_conditions(self):
# {outer_op: {inner_op: [inner < outer, inner == outer, inner > outer]}}
tests = {
'>=': {
'>=': [True, True, None],
'>': [True, None, None],
'<': [False, False, None],
'<=': [False, None, None],
'=': [False, None, None],
'==': [False, None, None],
'!=': [True, None, None],
},
'>': {
'>=': [True, True, None],
'>': [True, True, None],
'<': [False, False, None],
'<=': [False, False, None],
'=': [False, False, None],
'==': [False, False, None],
'!=': [True, True, None],
},
'<': {
'>=': [None, False, False],
'>': [None, False, False],
'<': [None, True, True],
'<=': [None, True, True],
'=': [None, False, False],
'==': [None, False, False],
'!=': [None, True, True],
},
'<=': {
'>=': [None, None, False],
'>': [None, False, False],
'<': [None, None, True],
'<=': [None, True, True],
'=': [None, None, False],
'==': [None, None, False],
'!=': [None, None, True],
},
'=': {
'>=': [True, True, False],
'>': [True, False, False],
'<': [False, False, True],
'<=': [False, True, True],
'=': [False, True, False],
'==': [False, True, False],
'!=': [True, False, True],
},
'==': {
'>=': [True, True, False],
'>': [True, False, False],
'<': [False, False, True],
'<=': [False, True, True],
'=': [False, True, False],
'==': [False, True, False],
'!=': [True, False, True],
},
'!=': {
'>=': [None, None, None],
'>': [None, None, None],
'<': [None, None, None],
'<=': [None, None, None],
'=': [None, False, None],
'==': [None, False, None],
'!=': [None, True, None],
},
}
sufs = ('', '.0')
for outer_op, inner in tests.items():
for inner_op, results in inner.items():
for inner_val, result in zip((40, 50, 60), results):
for outer_ext, inner_ext in itertools.product(sufs, sufs):
self.assertEqual(mesonbuild.mesonlib.version_compare_conditions(f'{outer_op}0.50{outer_ext}',
f'{inner_op}0.{inner_val}{inner_ext}'),
result)

def test_msvc_toolset_version(self):
'''
Ensure that the toolset version returns the correct value for this MSVC
Expand Down
Loading