aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-06-27 04:12:17 -0400
committerGitHub <noreply@github.com>2017-06-27 04:12:17 -0400
commit80ebc916f78ad736494ffee49f100134954acc2c (patch)
tree724f8c7390efd0536fa3a37082414a359ae9fd1f
parentee8fcd5f6729c0481c18309d6ad2beb0fdc67792 (diff)
parent3262be23dc3f01923a1d162a5914ba29f05416b7 (diff)
downloadmeson-80ebc916f78ad736494ffee49f100134954acc2c.zip
meson-80ebc916f78ad736494ffee49f100134954acc2c.tar.gz
meson-80ebc916f78ad736494ffee49f100134954acc2c.tar.bz2
Merge pull request #2001 from mesonbuild/kwcheck
Decorator to check for unknown keyword arguments
-rw-r--r--docs/markdown/Release-notes-for-0.42.0.md7
-rw-r--r--mesonbuild/interpreter.py235
-rw-r--r--mesonbuild/interpreterbase.py60
-rw-r--r--mesonbuild/modules/__init__.py16
-rw-r--r--mesonbuild/modules/gnome.py22
-rw-r--r--mesonbuild/modules/i18n.py4
-rw-r--r--mesonbuild/modules/modtest.py2
-rw-r--r--mesonbuild/modules/pkgconfig.py4
-rw-r--r--mesonbuild/modules/python3.py13
-rw-r--r--mesonbuild/modules/qt4.py2
-rw-r--r--mesonbuild/modules/qt5.py2
-rw-r--r--mesonbuild/modules/rpm.py2
-rw-r--r--mesonbuild/modules/windows.py2
-rw-r--r--test cases/failing/57 kwarg in module/meson.build5
14 files changed, 283 insertions, 93 deletions
diff --git a/docs/markdown/Release-notes-for-0.42.0.md b/docs/markdown/Release-notes-for-0.42.0.md
index dcb86c3..7c66870 100644
--- a/docs/markdown/Release-notes-for-0.42.0.md
+++ b/docs/markdown/Release-notes-for-0.42.0.md
@@ -11,3 +11,10 @@ short-description: Release notes for 0.42 (preliminary)
Creating distribution tarballs can now be made out of projects based on
Mercurial. As before, this remains possible only with the Ninja backend.
+
+## Keyword argument verification
+
+Meson will now check the keyword arguments used when calling any function
+and print a warning if any of the keyword arguments is not known. In the
+future this will become a hard error.
+
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index b08de18..916529f 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1,5 +1,4 @@
# Copyright 2012-2017 The Meson development team
-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -26,7 +25,7 @@ from .mesonlib import FileMode, Popen_safe, get_meson_script
from .dependencies import ExternalProgram
from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase
-from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs
+from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs
from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode
from .interpreterbase import InterpreterObject, MutableInterpreterObject
from .modules import ModuleReturnValue
@@ -1213,6 +1212,95 @@ class MesonMain(InterpreterObject):
return args[1]
raise InterpreterException('Unknown cross property: %s.' % propname)
+pch_kwargs = set(['c_pch', 'cpp_pch'])
+
+lang_arg_kwargs = set(['c_args',
+ 'cpp_args',
+ 'd_args',
+ 'fortran_args',
+ 'java_args',
+ 'objc_args',
+ 'objcpp_args',
+ 'rust_args',
+ 'vala_args',
+ ])
+
+vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi'])
+rust_kwargs = set(['rust_crate_type'])
+cs_kwargs = set(['resources'])
+
+buildtarget_kwargs = set(['build_by_default',
+ 'dependencies',
+ 'extra_files',
+ 'gui_app',
+ 'link_with',
+ 'link_whole',
+ 'link_args',
+ 'link_depends',
+ 'include_directories',
+ 'install',
+ 'install_rpath',
+ 'install_dir',
+ 'name_prefix',
+ 'name_suffix',
+ 'native',
+ 'objects',
+ 'override_options',
+ 'pic',
+ 'sources',
+ 'vs_module_defs',
+ ])
+
+build_target_common_kwargs = (
+ buildtarget_kwargs |
+ lang_arg_kwargs |
+ pch_kwargs |
+ vala_kwargs |
+ rust_kwargs |
+ cs_kwargs)
+
+exe_kwargs = set()
+exe_kwargs.update(build_target_common_kwargs)
+
+shlib_kwargs = (build_target_common_kwargs) | {'version', 'soversion'}
+shmod_kwargs = shlib_kwargs
+stlib_kwargs = shlib_kwargs
+
+jar_kwargs = exe_kwargs.copy()
+jar_kwargs.update(['main_class'])
+
+build_target_kwargs = exe_kwargs.copy()
+build_target_kwargs.update(['target_type'])
+
+permitted_kwargs = {'add_global_arguments': {'language'},
+ 'add_languages': {'required'},
+ 'add_project_arguments': {'language'},
+ 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
+ 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
+ 'build_target': build_target_kwargs,
+ 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'},
+ 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
+ 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'},
+ 'executable': exe_kwargs,
+ 'find_program': {'required'},
+ 'generator': {'arguments', 'output', 'depfile'},
+ 'include_directories': {'is_system'},
+ 'install_data': {'install_dir', 'install_mode', 'sources'},
+ 'install_headers': {'install_dir', 'subdir'},
+ 'install_man': {'install_dir'},
+ 'install_subdir': {'install_dir', 'install_mode'},
+ 'jar': jar_kwargs,
+ 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
+ 'run_target': {'command', 'depends'},
+ 'shared_library': shlib_kwargs,
+ 'shared_module': shmod_kwargs,
+ 'static_library': stlib_kwargs,
+ 'subproject': {'version', 'default_options'},
+ 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'},
+ 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'},
+ }
+
+
class Interpreter(InterpreterBase):
def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects',
@@ -1254,53 +1342,53 @@ class Interpreter(InterpreterBase):
self.build_def_files = [os.path.join(self.subdir, environment.build_filename)]
def build_func_dict(self):
- self.funcs.update({'project': self.func_project,
- 'message': self.func_message,
- 'error': self.func_error,
- 'executable': self.func_executable,
- 'dependency': self.func_dependency,
- 'static_library': self.func_static_lib,
- 'shared_library': self.func_shared_lib,
- 'shared_module': self.func_shared_module,
- 'library': self.func_library,
- 'jar': self.func_jar,
- 'build_target': self.func_build_target,
- 'custom_target': self.func_custom_target,
- 'run_target': self.func_run_target,
- 'generator': self.func_generator,
- 'test': self.func_test,
- 'benchmark': self.func_benchmark,
- 'install_headers': self.func_install_headers,
- 'install_man': self.func_install_man,
- 'subdir': self.func_subdir,
- 'install_data': self.func_install_data,
- 'install_subdir': self.func_install_subdir,
- 'configure_file': self.func_configure_file,
- 'include_directories': self.func_include_directories,
- 'add_global_arguments': self.func_add_global_arguments,
+ self.funcs.update({'add_global_arguments': self.func_add_global_arguments,
'add_project_arguments': self.func_add_project_arguments,
'add_global_link_arguments': self.func_add_global_link_arguments,
'add_project_link_arguments': self.func_add_project_link_arguments,
'add_test_setup': self.func_add_test_setup,
'add_languages': self.func_add_languages,
- 'find_program': self.func_find_program,
- 'find_library': self.func_find_library,
+ 'assert': self.func_assert,
+ 'benchmark': self.func_benchmark,
+ 'build_target': self.func_build_target,
'configuration_data': self.func_configuration_data,
- 'run_command': self.func_run_command,
+ 'configure_file': self.func_configure_file,
+ 'custom_target': self.func_custom_target,
+ 'declare_dependency': self.func_declare_dependency,
+ 'dependency': self.func_dependency,
+ 'environment': self.func_environment,
+ 'error': self.func_error,
+ 'executable': self.func_executable,
+ 'generator': self.func_generator,
'gettext': self.func_gettext,
- 'option': self.func_option,
'get_option': self.func_get_option,
- 'subproject': self.func_subproject,
- 'vcs_tag': self.func_vcs_tag,
- 'set_variable': self.func_set_variable,
- 'is_variable': self.func_is_variable,
'get_variable': self.func_get_variable,
- 'import': self.func_import,
'files': self.func_files,
- 'declare_dependency': self.func_declare_dependency,
- 'assert': self.func_assert,
- 'environment': self.func_environment,
+ 'find_library': self.func_find_library,
+ 'find_program': self.func_find_program,
+ 'include_directories': self.func_include_directories,
+ 'import': self.func_import,
+ 'install_data': self.func_install_data,
+ 'install_headers': self.func_install_headers,
+ 'install_man': self.func_install_man,
+ 'install_subdir': self.func_install_subdir,
+ 'is_variable': self.func_is_variable,
+ 'jar': self.func_jar,
'join_paths': self.func_join_paths,
+ 'library': self.func_library,
+ 'message': self.func_message,
+ 'option': self.func_option,
+ 'project': self.func_project,
+ 'run_target': self.func_run_target,
+ 'run_command': self.func_run_command,
+ 'set_variable': self.func_set_variable,
+ 'subdir': self.func_subdir,
+ 'subproject': self.func_subproject,
+ 'shared_library': self.func_shared_lib,
+ 'shared_module': self.func_shared_module,
+ 'static_library': self.func_static_lib,
+ 'test': self.func_test,
+ 'vcs_tag': self.func_vcs_tag,
})
def holderify(self, item):
@@ -1401,6 +1489,7 @@ class Interpreter(InterpreterBase):
def func_files(self, node, args, kwargs):
return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args]
+ @permittedKwargs(permitted_kwargs['declare_dependency'])
@noPosargs
def func_declare_dependency(self, node, args, kwargs):
version = kwargs.get('version', self.project_version)
@@ -1457,7 +1546,11 @@ class Interpreter(InterpreterBase):
if not isinstance(actual, wanted):
raise InvalidArguments('Incorrect argument type.')
+ @noKwargs
def func_run_command(self, node, args, kwargs):
+ return self.run_command_impl(node, args, kwargs)
+
+ def run_command_impl(self, node, args, kwargs, in_builddir=False):
if len(args) < 1:
raise InterpreterException('Not enough arguments')
cmd = args[0]
@@ -1490,9 +1583,6 @@ class Interpreter(InterpreterBase):
expanded_args.append(a.held_object.get_path())
else:
raise InterpreterException('Arguments ' + m.format(a))
- in_builddir = kwargs.get('in_builddir', False)
- if not isinstance(in_builddir, bool):
- raise InterpreterException('in_builddir must be boolean.')
return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir,
get_meson_script(self.environment, 'mesonintrospect'), in_builddir)
@@ -1503,6 +1593,7 @@ class Interpreter(InterpreterBase):
def func_option(self, nodes, args, kwargs):
raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.')
+ @permittedKwargs(permitted_kwargs['subproject'])
@stringArgs
def func_subproject(self, nodes, args, kwargs):
if len(args) != 1:
@@ -1631,6 +1722,7 @@ class Interpreter(InterpreterBase):
self.environment.cmd_line_options.projectoptions = newoptions
@stringArgs
+ @permittedKwargs(permitted_kwargs['project'])
def func_project(self, node, args, kwargs):
if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
@@ -1677,6 +1769,7 @@ class Interpreter(InterpreterBase):
if not self.is_subproject():
self.check_cross_stdlibs()
+ @permittedKwargs(permitted_kwargs['add_languages'])
@stringArgs
def func_add_languages(self, node, args, kwargs):
return self.add_languages(args, kwargs.get('required', True))
@@ -1820,6 +1913,7 @@ class Interpreter(InterpreterBase):
break
self.coredata.base_options[optname] = oobj
+ @permittedKwargs(permitted_kwargs['find_program'])
def func_find_program(self, node, args, kwargs):
if not args:
raise InterpreterException('No program name specified.')
@@ -2000,15 +2094,19 @@ class Interpreter(InterpreterBase):
mlog.bold(name))
return dep
+ @permittedKwargs(permitted_kwargs['executable'])
def func_executable(self, node, args, kwargs):
return self.build_target(node, args, kwargs, ExecutableHolder)
+ @permittedKwargs(permitted_kwargs['static_library'])
def func_static_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, StaticLibraryHolder)
+ @permittedKwargs(permitted_kwargs['shared_library'])
def func_shared_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedLibraryHolder)
+ @permittedKwargs(permitted_kwargs['shared_module'])
def func_shared_module(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedModuleHolder)
@@ -2017,9 +2115,12 @@ class Interpreter(InterpreterBase):
return self.func_shared_lib(node, args, kwargs)
return self.func_static_lib(node, args, kwargs)
+ @permittedKwargs(permitted_kwargs['jar'])
def func_jar(self, node, args, kwargs):
+ kwargs['target_type'] = 'jar'
return self.build_target(node, args, kwargs, JarHolder)
+ @permittedKwargs(permitted_kwargs['build_target'])
def func_build_target(self, node, args, kwargs):
if 'target_type' not in kwargs:
raise InterpreterException('Missing target_type keyword argument')
@@ -2037,6 +2138,7 @@ class Interpreter(InterpreterBase):
else:
raise InterpreterException('Unknown target_type.')
+ @permittedKwargs(permitted_kwargs['vcs_tag'])
def func_vcs_tag(self, node, args, kwargs):
if 'input' not in kwargs or 'output' not in kwargs:
raise InterpreterException('Keyword arguments input and output must exist')
@@ -2075,6 +2177,7 @@ class Interpreter(InterpreterBase):
return self.func_custom_target(node, [kwargs['output']], kwargs)
@stringArgs
+ @permittedKwargs(permitted_kwargs['custom_target'])
def func_custom_target(self, node, args, kwargs):
if len(args) != 1:
raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name')
@@ -2083,6 +2186,7 @@ class Interpreter(InterpreterBase):
self.add_target(name, tg.held_object)
return tg
+ @permittedKwargs(permitted_kwargs['run_target'])
def func_run_target(self, node, args, kwargs):
global run_depr_printed
if len(args) > 1:
@@ -2133,14 +2237,17 @@ class Interpreter(InterpreterBase):
self.add_target(name, tg.held_object)
return tg
+ @permittedKwargs(permitted_kwargs['generator'])
def func_generator(self, node, args, kwargs):
gen = GeneratorHolder(self, args, kwargs)
self.generators.append(gen)
return gen
+ @permittedKwargs(permitted_kwargs['benchmark'])
def func_benchmark(self, node, args, kwargs):
self.add_test(node, args, kwargs, False)
+ @permittedKwargs(permitted_kwargs['test'])
def func_test(self, node, args, kwargs):
self.add_test(node, args, kwargs, True)
@@ -2210,12 +2317,14 @@ class Interpreter(InterpreterBase):
self.build.benchmarks.append(t)
mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='')
+ @permittedKwargs(permitted_kwargs['install_headers'])
def func_install_headers(self, node, args, kwargs):
source_files = self.source_strings_to_files(args)
h = Headers(source_files, kwargs)
self.build.headers.append(h)
return h
+ @permittedKwargs(permitted_kwargs['install_man'])
@stringArgs
def func_install_man(self, node, args, kwargs):
m = Man(self.subdir, args, kwargs)
@@ -2279,6 +2388,7 @@ class Interpreter(InterpreterBase):
'permissions arg to be a string or false')
return FileMode(*install_mode)
+ @permittedKwargs(permitted_kwargs['install_data'])
def func_install_data(self, node, args, kwargs):
kwsource = mesonlib.stringlistify(kwargs.get('sources', []))
raw_sources = args + kwsource
@@ -2298,6 +2408,7 @@ class Interpreter(InterpreterBase):
self.build.data.append(data.held_object)
return data
+ @permittedKwargs(permitted_kwargs['install_subdir'])
@stringArgs
def func_install_subdir(self, node, args, kwargs):
if len(args) != 1:
@@ -2312,6 +2423,7 @@ class Interpreter(InterpreterBase):
self.build.install_dirs.append(idir)
return idir
+ @permittedKwargs(permitted_kwargs['configure_file'])
def func_configure_file(self, node, args, kwargs):
if len(args) > 0:
raise InterpreterException("configure_file takes only keyword arguments.")
@@ -2387,7 +2499,7 @@ class Interpreter(InterpreterBase):
# Substitute @INPUT@, @OUTPUT@, etc here.
cmd = mesonlib.substitute_values(kwargs['command'], values)
mlog.log('Configuring', mlog.bold(output), 'with command')
- res = self.func_run_command(node, cmd, {'in_builddir': True})
+ res = self.run_command_impl(node, cmd, {}, True)
if res.returncode != 0:
raise InterpreterException('Running configure command failed.\n%s\n%s' %
(res.stdout, res.stderr))
@@ -2406,6 +2518,7 @@ class Interpreter(InterpreterBase):
self.build.data.append(build.Data([cfile], idir))
return mesonlib.File.from_built_file(self.subdir, output)
+ @permittedKwargs(permitted_kwargs['include_directories'])
@stringArgs
def func_include_directories(self, node, args, kwargs):
src_root = self.environment.get_source_dir()
@@ -2442,6 +2555,7 @@ different subdirectory.
i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system))
return i
+ @permittedKwargs(permitted_kwargs['add_test_setup'])
@stringArgs
def func_add_test_setup(self, node, args, kwargs):
if len(args) != 1:
@@ -2483,18 +2597,22 @@ different subdirectory.
# and just use the master project ones.
self.build.test_setups[setup_name] = setupobj
+ @permittedKwargs(permitted_kwargs['add_global_arguments'])
@stringArgs
def func_add_global_arguments(self, node, args, kwargs):
self.add_global_arguments(node, self.build.global_args, args, kwargs)
+ @noKwargs
@stringArgs
def func_add_global_link_arguments(self, node, args, kwargs):
self.add_global_arguments(node, self.build.global_link_args, args, kwargs)
+ @permittedKwargs(permitted_kwargs['add_project_arguments'])
@stringArgs
def func_add_project_arguments(self, node, args, kwargs):
self.add_project_arguments(node, self.build.projects_args, args, kwargs)
+ @noKwargs
@stringArgs
def func_add_project_link_arguments(self, node, args, kwargs):
self.add_project_arguments(node, self.build.projects_link_args, args, kwargs)
@@ -2531,6 +2649,8 @@ different subdirectory.
lang = lang.lower()
argsdict[lang] = argsdict.get(lang, []) + args
+ @noKwargs
+ @noPosargs
def func_environment(self, node, args, kwargs):
return EnvironmentVariablesHolder()
@@ -2684,3 +2804,34 @@ different subdirectory.
def is_subproject(self):
return self.subproject != ''
+
+ @noKwargs
+ def func_set_variable(self, node, args, kwargs):
+ if len(args) != 2:
+ raise InvalidCode('Set_variable takes two arguments.')
+ varname = args[0]
+ value = args[1]
+ self.set_variable(varname, value)
+
+ @noKwargs
+ def func_get_variable(self, node, args, kwargs):
+ if len(args) < 1 or len(args) > 2:
+ raise InvalidCode('Get_variable takes one or two arguments.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('First argument must be a string.')
+ try:
+ return self.variables[varname]
+ except KeyError:
+ pass
+ if len(args) == 2:
+ return args[1]
+ raise InterpreterException('Tried to get unknown variable "%s".' % varname)
+
+ @stringArgs
+ @noKwargs
+ def func_is_variable(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidCode('Is_variable takes two arguments.')
+ varname = args[0]
+ return varname in self.variables
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index fb87ea2..d44f71b 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -55,6 +55,19 @@ def stringArgs(f):
return f(self, node, args, kwargs)
return wrapped
+class permittedKwargs:
+
+ def __init__(self, permitted):
+ self.permitted = permitted
+
+ def __call__(self, f):
+ def wrapped(s, node, args, kwargs):
+ for k in kwargs:
+ if k not in self.permitted:
+ mlog.warning('Passed invalid keyword argument %s. This will become a hard error in the future.' % k)
+ return f(s, node, args, kwargs)
+ return wrapped
+
class InterpreterException(mesonlib.MesonException):
pass
@@ -578,52 +591,5 @@ To specify a keyword argument, use : instead of =.''')
return isinstance(value, (InterpreterObject, dependencies.Dependency,
str, int, list, mesonlib.File))
- def func_build_target(self, node, args, kwargs):
- if 'target_type' not in kwargs:
- raise InterpreterException('Missing target_type keyword argument')
- target_type = kwargs.pop('target_type')
- if target_type == 'executable':
- return self.func_executable(node, args, kwargs)
- elif target_type == 'shared_library':
- return self.func_shared_lib(node, args, kwargs)
- elif target_type == 'static_library':
- return self.func_static_lib(node, args, kwargs)
- elif target_type == 'library':
- return self.func_library(node, args, kwargs)
- elif target_type == 'jar':
- return self.func_jar(node, args, kwargs)
- else:
- raise InterpreterException('Unknown target_type.')
-
- def func_set_variable(self, node, args, kwargs):
- if len(args) != 2:
- raise InvalidCode('Set_variable takes two arguments.')
- varname = args[0]
- value = args[1]
- self.set_variable(varname, value)
-
-# @noKwargs
- def func_get_variable(self, node, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InvalidCode('Get_variable takes one or two arguments.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('First argument must be a string.')
- try:
- return self.variables[varname]
- except KeyError:
- pass
- if len(args) == 2:
- return args[1]
- raise InterpreterException('Tried to get unknown variable "%s".' % varname)
-
- @stringArgs
- @noKwargs
- def func_is_variable(self, node, args, kwargs):
- if len(args) != 1:
- raise InvalidCode('Is_variable takes two arguments.')
- varname = args[0]
- return varname in self.variables
-
def is_elementary_type(self, v):
return isinstance(v, (int, float, str, bool, list))
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index fde3b91..9d75525 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -2,10 +2,26 @@ import os
from .. import build
from .. import dependencies
+from .. import mlog
from ..mesonlib import MesonException
+from ..interpreterbase import permittedKwargs, noKwargs
+
+class permittedSnippetKwargs:
+
+ def __init__(self, permitted):
+ self.permitted = permitted
+
+ def __call__(self, f):
+ def wrapped(s, interpreter, state, args, kwargs):
+ for k in kwargs:
+ if k not in self.permitted:
+ mlog.warning('Passed invalid keyword argument %s. This will become a hard error in the future.' % k)
+ return f(s, interpreter, state, args, kwargs)
+ return wrapped
_found_programs = {}
+
class ExtensionModule:
def __init__(self):
self.snippets = set() # List of methods that operate only on the interpreter.
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 06a1fad..de66ef9 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -30,6 +30,7 @@ from .. import interpreter
from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget
from . import find_program, get_include_args
from . import ExtensionModule
+from . import noKwargs, permittedKwargs
# gresource compilation is broken due to the way
@@ -90,6 +91,8 @@ class GnomeModule(ExtensionModule):
mlog.bold('https://github.com/mesonbuild/meson/issues/1387'))
gdbuswarning_printed = True
+ @permittedKwargs({'source_dir', 'c_name', 'dependencies', 'export', 'gresource_bundle', 'install_header',
+ 'install', 'install_dir', 'extra_args'})
def compile_resources(self, state, args, kwargs):
self.__print_gresources_warning(state)
glib_version = self._get_native_glib_version(state)
@@ -377,6 +380,10 @@ class GnomeModule(ExtensionModule):
return cflags, ldflags, gi_includes
+ @permittedKwargs({'sources', 'nsversion', 'namespace', 'symbol_prefix', 'identifier_prefix',
+ 'export_packagse', 'includes', 'dependencies', 'link_with', 'include_directories',
+ 'install', 'install_dir_gir', 'install_dir_typelib', 'extra_args',
+ 'packages'})
def generate_gir(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Gir takes one argument')
@@ -595,6 +602,7 @@ class GnomeModule(ExtensionModule):
rv = [scan_target, typelib_target]
return ModuleReturnValue(rv, rv)
+ @noKwargs
def compile_schemas(self, state, args, kwargs):
if args:
raise MesonException('Compile_schemas does not take positional arguments.')
@@ -613,6 +621,7 @@ class GnomeModule(ExtensionModule):
target_g = build.CustomTarget(targetname, state.subdir, kwargs)
return ModuleReturnValue(target_g, [target_g])
+ @permittedKwargs({'sources', 'media', 'symlink_media', 'languages'})
def yelp(self, state, args, kwargs):
if len(args) < 1:
raise MesonException('Yelp requires a project id')
@@ -670,6 +679,10 @@ class GnomeModule(ExtensionModule):
rv = [inscript, pottarget, potarget]
return ModuleReturnValue(None, rv)
+ @permittedKwargs({'main_xml', 'main_sgml', 'src_dir', 'dependencies', 'install',
+ 'install_dir', 'scan_args', 'scanobjs_args', 'gobject_typesfile',
+ 'fixxref_args', 'html_args', 'html_assets', 'content_files',
+ 'mkdb_args'})
def gtkdoc(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Gtkdoc must have one positional argument.')
@@ -763,6 +776,7 @@ class GnomeModule(ExtensionModule):
return args
+ @noKwargs
def gtkdoc_html_dir(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Must have exactly one argument.')
@@ -792,6 +806,7 @@ class GnomeModule(ExtensionModule):
return []
+ @permittedKwargs({'interface_prefix', 'namespace', 'object_manager'})
def gdbus_codegen(self, state, args, kwargs):
if len(args) != 2:
raise MesonException('Gdbus_codegen takes two arguments, name and xml file.')
@@ -820,6 +835,9 @@ class GnomeModule(ExtensionModule):
ct = build.CustomTarget(target_name, state.subdir, custom_kwargs)
return ModuleReturnValue(ct, [ct])
+ @permittedKwargs({'sources', 'c_template', 'h_template', 'install_header', 'install_dir',
+ 'comments', 'identifier_prefix', 'symbol_prefix', 'eprod', 'vprod',
+ 'fhead', 'fprod', 'ftail', 'vhead', 'vtail', 'depends'})
def mkenums(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Mkenums requires one positional argument.')
@@ -932,6 +950,8 @@ class GnomeModule(ExtensionModule):
# https://github.com/mesonbuild/meson/issues/973
absolute_paths=True)
+ @permittedKwargs({'sources', 'prefix', 'install_header', 'install_dir', 'stdinc',
+ 'nostdinc', 'internal', 'skip_source', 'valist_marshallers'})
def genmarshal(self, state, args, kwargs):
if len(args) != 1:
raise MesonException(
@@ -1070,6 +1090,8 @@ class GnomeModule(ExtensionModule):
link_with += self._get_vapi_link_with(dep)
return link_with
+ @permittedKwargs({'sources', 'packages', 'metadata_dirs', 'gir_dirs',
+ 'vapi_dirs', 'install', 'install_dir'})
def generate_vapi(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('The library name is required')
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index c4e29cf..d35c7f1 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -20,6 +20,7 @@ from .. import coredata, mesonlib, build
from ..mesonlib import MesonException
from . import ModuleReturnValue
from . import ExtensionModule
+from . import permittedKwargs
PRESET_ARGS = {
'glib': [
@@ -55,6 +56,8 @@ class I18nModule(ExtensionModule):
src_dir = path.join(state.environment.get_source_dir(), state.subdir)
return [path.join(src_dir, d) for d in dirs]
+ @permittedKwargs({'languages', 'data_dirs', 'preset', 'args', 'po_dir', 'type',
+ 'input', 'output', 'install', 'install_dir'})
def merge_file(self, state, args, kwargs):
podir = kwargs.pop('po_dir', None)
if not podir:
@@ -78,6 +81,7 @@ class I18nModule(ExtensionModule):
ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs)
return ModuleReturnValue(ct, [ct])
+ @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages'})
def gettext(self, state, args, kwargs):
if len(args) != 1:
raise coredata.MesonException('Gettext requires one positional argument (package name).')
diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py
index 3e11b70..dd2f215 100644
--- a/mesonbuild/modules/modtest.py
+++ b/mesonbuild/modules/modtest.py
@@ -14,9 +14,11 @@
from . import ModuleReturnValue
from . import ExtensionModule
+from . import noKwargs
class TestModule(ExtensionModule):
+ @noKwargs
def print_hello(self, state, args, kwargs):
print('Hello from a Meson module')
rv = ModuleReturnValue(None, [])
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 09c615a..7b0bb83 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -19,6 +19,7 @@ from .. import mesonlib
from .. import mlog
from . import ModuleReturnValue
from . import ExtensionModule
+from . import permittedKwargs
class PkgConfigModule(ExtensionModule):
@@ -114,6 +115,9 @@ class PkgConfigModule(ExtensionModule):
processed_libs.append(l)
return processed_libs
+ @permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
+ 'subdirs', 'requires', 'requires_private', 'libraries_private',
+ 'install_dir', 'variables'})
def generate(self, state, args, kwargs):
if len(args) > 0:
raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.')
diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py
index 9f01043..6431047 100644
--- a/mesonbuild/modules/python3.py
+++ b/mesonbuild/modules/python3.py
@@ -18,6 +18,11 @@ from .. import mesonlib, dependencies
from . import ExtensionModule
from mesonbuild.modules import ModuleReturnValue
+from . import noKwargs, permittedSnippetKwargs
+from ..interpreter import shlib_kwargs
+
+mod_kwargs = set()
+mod_kwargs.update(shlib_kwargs)
class Python3Module(ExtensionModule):
@@ -25,6 +30,7 @@ class Python3Module(ExtensionModule):
super().__init__()
self.snippets.add('extension_module')
+ @permittedSnippetKwargs(mod_kwargs)
def extension_module(self, interpreter, state, args, kwargs):
if 'name_prefix' in kwargs:
raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.')
@@ -43,20 +49,19 @@ class Python3Module(ExtensionModule):
kwargs['name_suffix'] = suffix
return interpreter.func_shared_module(None, args, kwargs)
+ @noKwargs
def find_python(self, state, args, kwargs):
py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True)
return ModuleReturnValue(py3, [py3])
+ @noKwargs
def language_version(self, state, args, kwargs):
- if args or kwargs:
- raise mesonlib.MesonException('language_version() takes no arguments.')
return ModuleReturnValue(sysconfig.get_python_version(), [])
+ @noKwargs
def sysconfig_path(self, state, args, kwargs):
if len(args) != 1:
raise mesonlib.MesonException('sysconfig_path() requires passing the name of path to get.')
- if kwargs:
- raise mesonlib.MesonException('sysconfig_path() does not accept keywords.')
path_name = args[0]
valid_names = sysconfig.get_path_names()
if path_name not in valid_names:
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index 0386291..4056b6d 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -20,6 +20,7 @@ from ..dependencies import Qt4Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET
from . import ModuleReturnValue
+from . import permittedKwargs
class Qt4Module(ExtensionModule):
tools_detected = False
@@ -96,6 +97,7 @@ class Qt4Module(ExtensionModule):
except Exception:
return []
+ @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
rcc_files = kwargs.pop('qresources', [])
if not isinstance(rcc_files, list):
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index 6497694..6194a23 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -20,6 +20,7 @@ from ..dependencies import Qt5Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET
from . import ModuleReturnValue
+from . import permittedKwargs
class Qt5Module(ExtensionModule):
tools_detected = False
@@ -102,6 +103,7 @@ class Qt5Module(ExtensionModule):
except Exception:
return []
+ @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
rcc_files = kwargs.pop('qresources', [])
if not isinstance(rcc_files, list):
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
index 17396ae..b0a8db9 100644
--- a/mesonbuild/modules/rpm.py
+++ b/mesonbuild/modules/rpm.py
@@ -22,11 +22,13 @@ from .. import mlog
from . import GirTarget, TypelibTarget
from . import ModuleReturnValue
from . import ExtensionModule
+from . import noKwargs
import os
class RPMModule(ExtensionModule):
+ @noKwargs
def generate_spec_template(self, state, args, kwargs):
compiler_deps = set()
for compiler in state.compilers.values():
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 3fb0107..6fef5bb 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -20,6 +20,7 @@ from ..mesonlib import MesonException
from . import get_include_args
from . import ModuleReturnValue
from . import ExtensionModule
+from . import permittedKwargs
class WindowsModule(ExtensionModule):
@@ -29,6 +30,7 @@ class WindowsModule(ExtensionModule):
return compilers[l]
raise MesonException('Resource compilation requires a C or C++ compiler.')
+ @permittedKwargs({'args', 'include_directories'})
def compile_resources(self, state, args, kwargs):
comp = self.detect_compiler(state.compilers)
diff --git a/test cases/failing/57 kwarg in module/meson.build b/test cases/failing/57 kwarg in module/meson.build
new file mode 100644
index 0000000..b105db1
--- /dev/null
+++ b/test cases/failing/57 kwarg in module/meson.build
@@ -0,0 +1,5 @@
+project('module test', 'c')
+
+modtest = import('modtest', i_cause: 'a_build_failure')
+modtest.print_hello()
+