From df1970d3add1f673f06f6269b45e8833a0bfd5c1 Mon Sep 17 00:00:00 2001 From: Salamandar Date: Mon, 4 Jun 2018 14:32:02 +0200 Subject: [PATCH] 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 --- mesonbuild/build.py | 6 +--- mesonbuild/interpreter.py | 9 +++++ mesonbuild/interpreterbase.py | 62 ++++++++++++++++++++++++----------- mesonbuild/mesonlib.py | 27 ++++++++++----- 4 files changed, 70 insertions(+), 34 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6ee13d233..2b225212d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.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') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 2cf17524a..fa9ae8471 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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] diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 1c613453e..d5761cdaa 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -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 diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 21a6e29e2..c1225893e 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -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)