diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 804152675..8863ed00f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -25,7 +25,7 @@ from .mesonlib import FileMode, Popen_safe, listify, extract_as_list from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase -from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs +from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs, permittedMethodKwargs from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler from .modules import ModuleReturnValue @@ -718,9 +718,11 @@ class CompilerHolder(InterpreterObject): 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, }) + @permittedMethodKwargs({}) def version_method(self, args, kwargs): return self.compiler.version + @permittedMethodKwargs({}) def cmd_array_method(self, args, kwargs): return self.compiler.exelist @@ -760,6 +762,11 @@ class CompilerHolder(InterpreterObject): deps = final_deps return deps + @permittedMethodKwargs({ + 'prefix', + 'args', + 'dependencies', + }) def alignment_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Alignment method takes exactly one positional argument.') @@ -774,6 +781,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='') return result + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def run_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Run method takes exactly one positional argument.') @@ -799,9 +813,11 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" runs: ', h, sep='') return TryRunResultHolder(result) + @permittedMethodKwargs({}) def get_id_method(self, args, kwargs): return self.compiler.get_id() + @permittedMethodKwargs({}) def symbols_have_underscore_prefix_method(self, args, kwargs): ''' Check if the compiler prefixes _ (underscore) to global C symbols @@ -809,6 +825,7 @@ class CompilerHolder(InterpreterObject): ''' return self.compiler.symbols_have_underscore_prefix(self.environment) + @permittedMethodKwargs({}) def unittest_args_method(self, args, kwargs): ''' This function is deprecated and should not be used. @@ -818,6 +835,13 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language())) return self.compiler.get_feature_args({'unittest': 'true'}) + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_member_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('Has_member takes exactly two arguments.') @@ -839,6 +863,13 @@ class CompilerHolder(InterpreterObject): '" has member "', mlog.bold(membername), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_members_method(self, args, kwargs): check_stringlist(args) typename = args[0] @@ -859,6 +890,13 @@ class CompilerHolder(InterpreterObject): '" has members ', members, ': ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_function_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Has_function takes exactly one argument.') @@ -877,6 +915,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for function "', mlog.bold(funcname), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_type_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Has_type takes exactly one argument.') @@ -895,6 +940,16 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'low', + 'high', + 'guess', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def compute_int_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Compute_int takes exactly one argument.') @@ -918,6 +973,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Computing int of "%s": %d' % (expression, res)) return res + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def sizeof_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Sizeof takes exactly one argument.') @@ -932,6 +994,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for size of "%s": %d' % (element, esize)) return esize + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def get_define_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('get_define() takes exactly one argument.') @@ -946,6 +1015,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Fetching value of define "%s": %s' % (element, value)) return value + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def compiles_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('compiles method takes exactly one argument.') @@ -969,6 +1045,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" compiles: ', h, sep='') return result + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def links_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('links method takes exactly one argument.') @@ -992,6 +1075,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" links: ', h, sep='') return result + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_header_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('has_header method takes exactly one argument.') @@ -1010,6 +1100,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Has header "%s":' % hname, h) return haz + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_header_symbol_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('has_header_symbol method takes exactly two arguments.') @@ -1029,6 +1126,10 @@ class CompilerHolder(InterpreterObject): mlog.log('Header <{0}> has symbol "{1}":'.format(hname, symbol), h) return haz + @permittedMethodKwargs({ + 'required', + 'dirs', + }) def find_library_method(self, args, kwargs): # TODO add dependencies support? if len(args) != 1: @@ -1050,6 +1151,7 @@ class CompilerHolder(InterpreterObject): self.compiler.language) return ExternalLibraryHolder(lib) + @permittedMethodKwargs({}) def has_argument_method(self, args, kwargs): args = mesonlib.stringlistify(args) if len(args) != 1: @@ -1062,6 +1164,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h) return result + @permittedMethodKwargs({}) def has_multi_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.has_multi_arguments(args, self.environment) @@ -1075,6 +1178,7 @@ class CompilerHolder(InterpreterObject): h) return result + @permittedMethodKwargs({}) def get_supported_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.get_supported_arguments(args, self.environment) @@ -1090,6 +1194,7 @@ class CompilerHolder(InterpreterObject): h) return result + @permittedMethodKwargs({}) def first_supported_argument_method(self, args, kwargs): for i in mesonlib.stringlistify(args): if self.compiler.has_argument(i, self.environment): diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 0539b145c..c56e30b2e 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -80,6 +80,22 @@ class permittedKwargs: return wrapped +class permittedMethodKwargs: + + def __init__(self, permitted): + self.permitted = permitted + + def __call__(self, f): + @wraps(f) + def wrapped(obj, args, kwargs): + for k in kwargs: + if k not in self.permitted: + mlog.warning('''Passed invalid keyword argument "{}".'''.format(k)) + mlog.warning('This will become a hard error in the future.') + return f(obj, args, kwargs) + return wrapped + + class InterpreterException(mesonlib.MesonException): pass diff --git a/run_unittests.py b/run_unittests.py index 41cbf3931..d986a0dd4 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1735,6 +1735,16 @@ int main(int argc, char **argv) { ]: self.assertRegex(out, re.escape(expected)) + def test_permitted_method_kwargs(self): + tdir = os.path.join(self.unit_test_dir, '23 non-permitted kwargs') + out = self.init(tdir) + for expected in [ + r'WARNING: Passed invalid keyword argument "prefixxx".', + r'WARNING: Passed invalid keyword argument "argsxx".', + r'WARNING: Passed invalid keyword argument "invalidxx".', + ]: + self.assertRegex(out, re.escape(expected)) + def test_templates(self): ninja = detect_ninja() if ninja is None: diff --git a/test cases/unit/23 non-permitted kwargs/meson.build b/test cases/unit/23 non-permitted kwargs/meson.build new file mode 100644 index 000000000..9f7dc1f76 --- /dev/null +++ b/test cases/unit/23 non-permitted kwargs/meson.build @@ -0,0 +1,5 @@ +project('non-permitted kwargs', 'c') +cc = meson.get_compiler('c') +cc.has_header_symbol('stdio.h', 'printf', prefixxx: '#define XXX') +cc.links('int main(){}', argsxx: '') +cc.get_id(invalidxx: '')