Various bug fixes for FeatureNew

* Use _get_callee_args to unwrap function call arguments, needed for
  module functions.

* Move some FeatureNewKwargs from build.py to interpreter.py

* Print a summary for featurenew only if conflicts were found. The
  summary now only prints conflicting features.

* Report and store featurenew/featuredeprecated only once

* Fix version comparison: use le/ge and resize arrays to not fail on
  '0.47.0>=0.47'

Closes https://github.com/mesonbuild/meson/issues/3660
pull/3823/head
Salamandar 7 years ago committed by Nirbheek Chauhan
parent 5113eb14b9
commit df1970d3ad
  1. 6
      mesonbuild/build.py
  2. 9
      mesonbuild/interpreter.py
  3. 62
      mesonbuild/interpreterbase.py
  4. 27
      mesonbuild/mesonlib.py

@ -25,7 +25,7 @@ from .mesonlib import typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
from .mesonlib import for_windows, for_darwin, for_cygwin, for_android, has_path_sep
from .compilers import is_object, clink_langs, sort_clink, lang_suffixes
from .interpreterbase import FeatureNew, FeatureNewKwargs
from .interpreterbase import FeatureNew
pch_kwargs = set(['c_pch', 'cpp_pch'])
@ -332,9 +332,6 @@ a hard error in the future.''' % name)
myid = subdir_part + '@@' + myid
return myid
@FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories'])
@FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
@FeatureNewKwargs('build target', '0.40.0', ['build_by_default'])
def process_kwargs(self, kwargs):
if 'build_by_default' in kwargs:
self.build_by_default = kwargs['build_by_default']
@ -1095,7 +1092,6 @@ recommended as it is not supported on some platforms''')
return
class Generator:
@FeatureNewKwargs('generator', '0.43.0', ['capture'])
def __init__(self, args, kwargs):
if len(args) != 1:
raise InvalidArguments('Generator requires exactly one positional argument: the executable')

@ -504,6 +504,7 @@ class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
class GeneratorHolder(InterpreterObject, ObjectHolder):
@FeatureNewKwargs('generator', '0.43.0', ['capture'])
def __init__(self, interpreter, args, kwargs):
InterpreterObject.__init__(self)
self.interpreter = interpreter
@ -3792,6 +3793,14 @@ Try setting b_lundef to false instead.''')
raise InterpreterException('Unknown default_library value: %s.', default_library)
def build_target(self, node, args, kwargs, targetholder):
@FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories'])
@FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
@FeatureNewKwargs('build target', '0.40.0', ['build_by_default'])
def build_target_decorator_caller(self, node, args, kwargs):
return True
build_target_decorator_caller(self, node, args, kwargs)
if not args:
raise InterpreterException('Target does not have a name.')
name = args[0]

@ -127,6 +127,7 @@ class FeatureNew:
"""Checks for new features"""
# Shared across all instances
feature_versions = dict()
feature_warnings = False
def __init__(self, feature_name, version):
self.feature_name = feature_name
@ -135,24 +136,33 @@ class FeatureNew:
def add_called_feature(self):
if self.feature_version not in self.feature_versions:
self.feature_versions[self.feature_version] = set()
self.feature_versions[self.feature_version].add(self.feature_name)
if self.feature_name in self.feature_versions[self.feature_version]:
return False
else:
self.feature_versions[self.feature_version].add(self.feature_name)
return True
def called_features_report():
fv = FeatureNew.feature_versions
if fv:
print('Minimum version of features used:')
if FeatureNew.feature_warnings:
warning_str = 'Invalid minimum meson_version \'{}\' conflicts with:'\
.format(mesonlib.target_version)
fv = FeatureNew.feature_versions
for version in sorted(fv.keys()):
print('{}: {}'.format(version, fv[version]))
warning_str += '\n * {}: {}'.format(version, fv[version])
mlog.warning(warning_str)
def use(self):
self.add_called_feature()
tv = mesonlib.target_version
if tv == '':
return
if not mesonlib.version_compare_condition_with_min(tv, self.feature_version):
mlog.warning(
'''Project targetting \'{}\' but tried to use feature introduced in \'{}\': {}'''
.format(tv, self.feature_version, self.feature_name))
FeatureNew.feature_warnings = True
if self.add_called_feature():
mlog.warning(
'''Project targetting \'{}\' but tried to use feature introduced in \'{}\': {}'''
.format(tv, self.feature_version, self.feature_name)
)
def __call__(self, f):
@wraps(f)
@ -165,6 +175,7 @@ class FeatureDeprecated:
"""Checks for deprecated features"""
# Shared across all instances
feature_versions = dict()
feature_warnings = False
def __init__(self, feature_name, version):
self.feature_name = feature_name
@ -173,24 +184,33 @@ class FeatureDeprecated:
def add_called_feature(self):
if self.feature_version not in self.feature_versions:
self.feature_versions[self.feature_version] = set()
self.feature_versions[self.feature_version].add(self.feature_name)
if self.feature_name in self.feature_versions[self.feature_version]:
return False
else:
self.feature_versions[self.feature_version].add(self.feature_name)
return True
def called_features_report():
fv = FeatureDeprecated.feature_versions
if fv:
print('Deprecated features used:')
if FeatureDeprecated.feature_warnings:
warning_str = 'Deprecated features used:'\
.format(mesonlib.target_version)
fv = FeatureDeprecated.feature_versions
for version in sorted(fv.keys()):
print('{}: {}'.format(version, fv[version]))
warning_str += '\n * {}: {}'.format(version, fv[version])
mlog.warning(warning_str)
def use(self):
self.add_called_feature()
tv = mesonlib.target_version
if tv == '':
return
if not mesonlib.version_compare_condition_with_max(tv, self.feature_version):
mlog.warning(
'''Project targetting \'{}\' but tried to use feature deprecated since \'{}\': {}'''
.format(tv, self.feature_version, self.feature_name))
FeatureDeprecated.feature_warnings = True
if self.add_called_feature():
mlog.warning(
'''Project targetting \'{}\' but tried to use feature deprecated since \'{}\': {}'''
.format(tv, self.feature_version, self.feature_name)
)
def __call__(self, f):
@wraps(f)
@ -208,8 +228,9 @@ class FeatureNewKwargs:
def __call__(self, f):
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
s, node_or_state, args, call_kwargs = _get_callee_args(wrapped_args)
for arg in self.kwargs:
if arg in wrapped_kwargs:
if arg in call_kwargs:
FeatureNew(arg + ' arg in ' + self.feature_name, self.feature_version).use()
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
@ -223,8 +244,9 @@ class FeatureDeprecatedKwargs:
def __call__(self, f):
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
s, node_or_state, args, call_kwargs = _get_callee_args(wrapped_args)
for arg in self.kwargs:
if arg in wrapped_kwargs:
if arg in call_kwargs:
FeatureDeprecated(arg + ' arg in ' + self.feature_name, self.feature_version).use()
return f(*wrapped_args, **wrapped_kwargs)
return wrapped

@ -396,6 +396,12 @@ def grab_leading_numbers(vstr, strict=False):
break
return result
def make_same_len(listA, listB):
maxlen = max(len(listA), len(listB))
for i in listA, listB:
for n in range(len(i), maxlen):
i.append(0)
numpart = re.compile('[0-9.]+')
def version_compare(vstr1, vstr2, strict=False):
@ -429,6 +435,7 @@ def version_compare(vstr1, vstr2, strict=False):
cmpop = operator.eq
varr1 = grab_leading_numbers(vstr1, strict)
varr2 = grab_leading_numbers(vstr2, strict)
make_same_len(varr1, varr2)
return cmpop(varr1, varr2)
def version_compare_many(vstr1, conditions):
@ -451,7 +458,7 @@ def version_compare_condition_with_min(condition, minimum):
raise MesonException(msg.format(minimum))
minimum = match.group(0)
if condition.startswith('>='):
cmpop = operator.lt
cmpop = operator.le
condition = condition[2:]
elif condition.startswith('<='):
return True
@ -460,10 +467,10 @@ def version_compare_condition_with_min(condition, minimum):
return True
condition = condition[2:]
elif condition.startswith('=='):
cmpop = operator.lt
cmpop = operator.le
condition = condition[2:]
elif condition.startswith('='):
cmpop = operator.lt
cmpop = operator.le
condition = condition[1:]
elif condition.startswith('>'):
cmpop = operator.lt
@ -472,9 +479,10 @@ def version_compare_condition_with_min(condition, minimum):
return True
condition = condition[2:]
else:
cmpop = operator.eq
cmpop = operator.le
varr1 = grab_leading_numbers(minimum, True)
varr2 = grab_leading_numbers(condition, True)
make_same_len(varr1, varr2)
return cmpop(varr1, varr2)
def version_compare_condition_with_max(condition, maximum):
@ -487,27 +495,28 @@ def version_compare_condition_with_max(condition, maximum):
return False
condition = condition[2:]
elif condition.startswith('<='):
cmpop = operator.lt
cmpop = operator.ge
condition = condition[2:]
elif condition.startswith('!='):
return False
condition = condition[2:]
elif condition.startswith('=='):
cmpop = operator.lt
cmpop = operator.ge
condition = condition[2:]
elif condition.startswith('='):
cmpop = operator.lt
cmpop = operator.ge
condition = condition[1:]
elif condition.startswith('>'):
return False
condition = condition[1:]
elif condition.startswith('<'):
cmpop = operator.lt
cmpop = operator.gt
condition = condition[2:]
else:
cmpop = operator.eq
cmpop = operator.ge
varr1 = grab_leading_numbers(maximum, True)
varr2 = grab_leading_numbers(condition, True)
make_same_len(varr1, varr2)
return cmpop(varr1, varr2)

Loading…
Cancel
Save