Merge pull request #2001 from mesonbuild/kwcheck

Decorator to check for unknown keyword arguments
pull/1995/merge
Jussi Pakkanen 8 years ago committed by GitHub
commit 80ebc916f7
  1. 7
      docs/markdown/Release-notes-for-0.42.0.md
  2. 235
      mesonbuild/interpreter.py
  3. 60
      mesonbuild/interpreterbase.py
  4. 16
      mesonbuild/modules/__init__.py
  5. 22
      mesonbuild/modules/gnome.py
  6. 4
      mesonbuild/modules/i18n.py
  7. 2
      mesonbuild/modules/modtest.py
  8. 4
      mesonbuild/modules/pkgconfig.py
  9. 13
      mesonbuild/modules/python3.py
  10. 2
      mesonbuild/modules/qt4.py
  11. 2
      mesonbuild/modules/qt5.py
  12. 2
      mesonbuild/modules/rpm.py
  13. 2
      mesonbuild/modules/windows.py
  14. 5
      test cases/failing/57 kwarg in module/meson.build

@ -11,3 +11,10 @@ short-description: Release notes for 0.42 (preliminary)
Creating distribution tarballs can now be made out of projects based on Creating distribution tarballs can now be made out of projects based on
Mercurial. As before, this remains possible only with the Ninja backend. 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.

@ -1,5 +1,4 @@
# Copyright 2012-2017 The Meson development team # Copyright 2012-2017 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # 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 ExternalProgram
from .dependencies import InternalDependency, Dependency, DependencyException from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase 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 InterpreterException, InvalidArguments, InvalidCode
from .interpreterbase import InterpreterObject, MutableInterpreterObject from .interpreterbase import InterpreterObject, MutableInterpreterObject
from .modules import ModuleReturnValue from .modules import ModuleReturnValue
@ -1213,6 +1212,95 @@ class MesonMain(InterpreterObject):
return args[1] return args[1]
raise InterpreterException('Unknown cross property: %s.' % propname) 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): class Interpreter(InterpreterBase):
def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects', 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)] self.build_def_files = [os.path.join(self.subdir, environment.build_filename)]
def build_func_dict(self): def build_func_dict(self):
self.funcs.update({'project': self.func_project, self.funcs.update({'add_global_arguments': self.func_add_global_arguments,
'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,
'add_project_arguments': self.func_add_project_arguments, 'add_project_arguments': self.func_add_project_arguments,
'add_global_link_arguments': self.func_add_global_link_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments,
'add_project_link_arguments': self.func_add_project_link_arguments, 'add_project_link_arguments': self.func_add_project_link_arguments,
'add_test_setup': self.func_add_test_setup, 'add_test_setup': self.func_add_test_setup,
'add_languages': self.func_add_languages, 'add_languages': self.func_add_languages,
'find_program': self.func_find_program, 'assert': self.func_assert,
'find_library': self.func_find_library, 'benchmark': self.func_benchmark,
'build_target': self.func_build_target,
'configuration_data': self.func_configuration_data, '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, 'gettext': self.func_gettext,
'option': self.func_option,
'get_option': self.func_get_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, 'get_variable': self.func_get_variable,
'import': self.func_import,
'files': self.func_files, 'files': self.func_files,
'declare_dependency': self.func_declare_dependency, 'find_library': self.func_find_library,
'assert': self.func_assert, 'find_program': self.func_find_program,
'environment': self.func_environment, '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, '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): def holderify(self, item):
@ -1401,6 +1489,7 @@ class Interpreter(InterpreterBase):
def func_files(self, node, args, kwargs): def func_files(self, node, args, kwargs):
return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args] return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args]
@permittedKwargs(permitted_kwargs['declare_dependency'])
@noPosargs @noPosargs
def func_declare_dependency(self, node, args, kwargs): def func_declare_dependency(self, node, args, kwargs):
version = kwargs.get('version', self.project_version) version = kwargs.get('version', self.project_version)
@ -1457,7 +1546,11 @@ class Interpreter(InterpreterBase):
if not isinstance(actual, wanted): if not isinstance(actual, wanted):
raise InvalidArguments('Incorrect argument type.') raise InvalidArguments('Incorrect argument type.')
@noKwargs
def func_run_command(self, node, args, kwargs): 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: if len(args) < 1:
raise InterpreterException('Not enough arguments') raise InterpreterException('Not enough arguments')
cmd = args[0] cmd = args[0]
@ -1490,9 +1583,6 @@ class Interpreter(InterpreterBase):
expanded_args.append(a.held_object.get_path()) expanded_args.append(a.held_object.get_path())
else: else:
raise InterpreterException('Arguments ' + m.format(a)) 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, return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir,
get_meson_script(self.environment, 'mesonintrospect'), in_builddir) get_meson_script(self.environment, 'mesonintrospect'), in_builddir)
@ -1503,6 +1593,7 @@ class Interpreter(InterpreterBase):
def func_option(self, nodes, args, kwargs): 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.') raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.')
@permittedKwargs(permitted_kwargs['subproject'])
@stringArgs @stringArgs
def func_subproject(self, nodes, args, kwargs): def func_subproject(self, nodes, args, kwargs):
if len(args) != 1: if len(args) != 1:
@ -1631,6 +1722,7 @@ class Interpreter(InterpreterBase):
self.environment.cmd_line_options.projectoptions = newoptions self.environment.cmd_line_options.projectoptions = newoptions
@stringArgs @stringArgs
@permittedKwargs(permitted_kwargs['project'])
def func_project(self, node, args, kwargs): def func_project(self, node, args, kwargs):
if len(args) < 1: if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.') 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(): if not self.is_subproject():
self.check_cross_stdlibs() self.check_cross_stdlibs()
@permittedKwargs(permitted_kwargs['add_languages'])
@stringArgs @stringArgs
def func_add_languages(self, node, args, kwargs): def func_add_languages(self, node, args, kwargs):
return self.add_languages(args, kwargs.get('required', True)) return self.add_languages(args, kwargs.get('required', True))
@ -1820,6 +1913,7 @@ class Interpreter(InterpreterBase):
break break
self.coredata.base_options[optname] = oobj self.coredata.base_options[optname] = oobj
@permittedKwargs(permitted_kwargs['find_program'])
def func_find_program(self, node, args, kwargs): def func_find_program(self, node, args, kwargs):
if not args: if not args:
raise InterpreterException('No program name specified.') raise InterpreterException('No program name specified.')
@ -2000,15 +2094,19 @@ class Interpreter(InterpreterBase):
mlog.bold(name)) mlog.bold(name))
return dep return dep
@permittedKwargs(permitted_kwargs['executable'])
def func_executable(self, node, args, kwargs): def func_executable(self, node, args, kwargs):
return self.build_target(node, args, kwargs, ExecutableHolder) return self.build_target(node, args, kwargs, ExecutableHolder)
@permittedKwargs(permitted_kwargs['static_library'])
def func_static_lib(self, node, args, kwargs): def func_static_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, StaticLibraryHolder) return self.build_target(node, args, kwargs, StaticLibraryHolder)
@permittedKwargs(permitted_kwargs['shared_library'])
def func_shared_lib(self, node, args, kwargs): def func_shared_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedLibraryHolder) return self.build_target(node, args, kwargs, SharedLibraryHolder)
@permittedKwargs(permitted_kwargs['shared_module'])
def func_shared_module(self, node, args, kwargs): def func_shared_module(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedModuleHolder) 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_shared_lib(node, args, kwargs)
return self.func_static_lib(node, args, kwargs) return self.func_static_lib(node, args, kwargs)
@permittedKwargs(permitted_kwargs['jar'])
def func_jar(self, node, args, kwargs): def func_jar(self, node, args, kwargs):
kwargs['target_type'] = 'jar'
return self.build_target(node, args, kwargs, JarHolder) return self.build_target(node, args, kwargs, JarHolder)
@permittedKwargs(permitted_kwargs['build_target'])
def func_build_target(self, node, args, kwargs): def func_build_target(self, node, args, kwargs):
if 'target_type' not in kwargs: if 'target_type' not in kwargs:
raise InterpreterException('Missing target_type keyword argument') raise InterpreterException('Missing target_type keyword argument')
@ -2037,6 +2138,7 @@ class Interpreter(InterpreterBase):
else: else:
raise InterpreterException('Unknown target_type.') raise InterpreterException('Unknown target_type.')
@permittedKwargs(permitted_kwargs['vcs_tag'])
def func_vcs_tag(self, node, args, kwargs): def func_vcs_tag(self, node, args, kwargs):
if 'input' not in kwargs or 'output' not in kwargs: if 'input' not in kwargs or 'output' not in kwargs:
raise InterpreterException('Keyword arguments input and output must exist') 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) return self.func_custom_target(node, [kwargs['output']], kwargs)
@stringArgs @stringArgs
@permittedKwargs(permitted_kwargs['custom_target'])
def func_custom_target(self, node, args, kwargs): def func_custom_target(self, node, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name') 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) self.add_target(name, tg.held_object)
return tg return tg
@permittedKwargs(permitted_kwargs['run_target'])
def func_run_target(self, node, args, kwargs): def func_run_target(self, node, args, kwargs):
global run_depr_printed global run_depr_printed
if len(args) > 1: if len(args) > 1:
@ -2133,14 +2237,17 @@ class Interpreter(InterpreterBase):
self.add_target(name, tg.held_object) self.add_target(name, tg.held_object)
return tg return tg
@permittedKwargs(permitted_kwargs['generator'])
def func_generator(self, node, args, kwargs): def func_generator(self, node, args, kwargs):
gen = GeneratorHolder(self, args, kwargs) gen = GeneratorHolder(self, args, kwargs)
self.generators.append(gen) self.generators.append(gen)
return gen return gen
@permittedKwargs(permitted_kwargs['benchmark'])
def func_benchmark(self, node, args, kwargs): def func_benchmark(self, node, args, kwargs):
self.add_test(node, args, kwargs, False) self.add_test(node, args, kwargs, False)
@permittedKwargs(permitted_kwargs['test'])
def func_test(self, node, args, kwargs): def func_test(self, node, args, kwargs):
self.add_test(node, args, kwargs, True) self.add_test(node, args, kwargs, True)
@ -2210,12 +2317,14 @@ class Interpreter(InterpreterBase):
self.build.benchmarks.append(t) self.build.benchmarks.append(t)
mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='') mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='')
@permittedKwargs(permitted_kwargs['install_headers'])
def func_install_headers(self, node, args, kwargs): def func_install_headers(self, node, args, kwargs):
source_files = self.source_strings_to_files(args) source_files = self.source_strings_to_files(args)
h = Headers(source_files, kwargs) h = Headers(source_files, kwargs)
self.build.headers.append(h) self.build.headers.append(h)
return h return h
@permittedKwargs(permitted_kwargs['install_man'])
@stringArgs @stringArgs
def func_install_man(self, node, args, kwargs): def func_install_man(self, node, args, kwargs):
m = Man(self.subdir, args, kwargs) m = Man(self.subdir, args, kwargs)
@ -2279,6 +2388,7 @@ class Interpreter(InterpreterBase):
'permissions arg to be a string or false') 'permissions arg to be a string or false')
return FileMode(*install_mode) return FileMode(*install_mode)
@permittedKwargs(permitted_kwargs['install_data'])
def func_install_data(self, node, args, kwargs): def func_install_data(self, node, args, kwargs):
kwsource = mesonlib.stringlistify(kwargs.get('sources', [])) kwsource = mesonlib.stringlistify(kwargs.get('sources', []))
raw_sources = args + kwsource raw_sources = args + kwsource
@ -2298,6 +2408,7 @@ class Interpreter(InterpreterBase):
self.build.data.append(data.held_object) self.build.data.append(data.held_object)
return data return data
@permittedKwargs(permitted_kwargs['install_subdir'])
@stringArgs @stringArgs
def func_install_subdir(self, node, args, kwargs): def func_install_subdir(self, node, args, kwargs):
if len(args) != 1: if len(args) != 1:
@ -2312,6 +2423,7 @@ class Interpreter(InterpreterBase):
self.build.install_dirs.append(idir) self.build.install_dirs.append(idir)
return idir return idir
@permittedKwargs(permitted_kwargs['configure_file'])
def func_configure_file(self, node, args, kwargs): def func_configure_file(self, node, args, kwargs):
if len(args) > 0: if len(args) > 0:
raise InterpreterException("configure_file takes only keyword arguments.") raise InterpreterException("configure_file takes only keyword arguments.")
@ -2387,7 +2499,7 @@ class Interpreter(InterpreterBase):
# Substitute @INPUT@, @OUTPUT@, etc here. # Substitute @INPUT@, @OUTPUT@, etc here.
cmd = mesonlib.substitute_values(kwargs['command'], values) cmd = mesonlib.substitute_values(kwargs['command'], values)
mlog.log('Configuring', mlog.bold(output), 'with command') 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: if res.returncode != 0:
raise InterpreterException('Running configure command failed.\n%s\n%s' % raise InterpreterException('Running configure command failed.\n%s\n%s' %
(res.stdout, res.stderr)) (res.stdout, res.stderr))
@ -2406,6 +2518,7 @@ class Interpreter(InterpreterBase):
self.build.data.append(build.Data([cfile], idir)) self.build.data.append(build.Data([cfile], idir))
return mesonlib.File.from_built_file(self.subdir, output) return mesonlib.File.from_built_file(self.subdir, output)
@permittedKwargs(permitted_kwargs['include_directories'])
@stringArgs @stringArgs
def func_include_directories(self, node, args, kwargs): def func_include_directories(self, node, args, kwargs):
src_root = self.environment.get_source_dir() src_root = self.environment.get_source_dir()
@ -2442,6 +2555,7 @@ different subdirectory.
i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system)) i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system))
return i return i
@permittedKwargs(permitted_kwargs['add_test_setup'])
@stringArgs @stringArgs
def func_add_test_setup(self, node, args, kwargs): def func_add_test_setup(self, node, args, kwargs):
if len(args) != 1: if len(args) != 1:
@ -2483,18 +2597,22 @@ different subdirectory.
# and just use the master project ones. # and just use the master project ones.
self.build.test_setups[setup_name] = setupobj self.build.test_setups[setup_name] = setupobj
@permittedKwargs(permitted_kwargs['add_global_arguments'])
@stringArgs @stringArgs
def func_add_global_arguments(self, node, args, kwargs): def func_add_global_arguments(self, node, args, kwargs):
self.add_global_arguments(node, self.build.global_args, args, kwargs) self.add_global_arguments(node, self.build.global_args, args, kwargs)
@noKwargs
@stringArgs @stringArgs
def func_add_global_link_arguments(self, node, args, kwargs): def func_add_global_link_arguments(self, node, args, kwargs):
self.add_global_arguments(node, self.build.global_link_args, args, kwargs) self.add_global_arguments(node, self.build.global_link_args, args, kwargs)
@permittedKwargs(permitted_kwargs['add_project_arguments'])
@stringArgs @stringArgs
def func_add_project_arguments(self, node, args, kwargs): def func_add_project_arguments(self, node, args, kwargs):
self.add_project_arguments(node, self.build.projects_args, args, kwargs) self.add_project_arguments(node, self.build.projects_args, args, kwargs)
@noKwargs
@stringArgs @stringArgs
def func_add_project_link_arguments(self, node, args, kwargs): def func_add_project_link_arguments(self, node, args, kwargs):
self.add_project_arguments(node, self.build.projects_link_args, args, kwargs) self.add_project_arguments(node, self.build.projects_link_args, args, kwargs)
@ -2531,6 +2649,8 @@ different subdirectory.
lang = lang.lower() lang = lang.lower()
argsdict[lang] = argsdict.get(lang, []) + args argsdict[lang] = argsdict.get(lang, []) + args
@noKwargs
@noPosargs
def func_environment(self, node, args, kwargs): def func_environment(self, node, args, kwargs):
return EnvironmentVariablesHolder() return EnvironmentVariablesHolder()
@ -2684,3 +2804,34 @@ different subdirectory.
def is_subproject(self): def is_subproject(self):
return self.subproject != '' 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

@ -55,6 +55,19 @@ def stringArgs(f):
return f(self, node, args, kwargs) return f(self, node, args, kwargs)
return wrapped 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): class InterpreterException(mesonlib.MesonException):
pass pass
@ -578,52 +591,5 @@ To specify a keyword argument, use : instead of =.''')
return isinstance(value, (InterpreterObject, dependencies.Dependency, return isinstance(value, (InterpreterObject, dependencies.Dependency,
str, int, list, mesonlib.File)) 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): def is_elementary_type(self, v):
return isinstance(v, (int, float, str, bool, list)) return isinstance(v, (int, float, str, bool, list))

@ -2,10 +2,26 @@ import os
from .. import build from .. import build
from .. import dependencies from .. import dependencies
from .. import mlog
from ..mesonlib import MesonException 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 = {} _found_programs = {}
class ExtensionModule: class ExtensionModule:
def __init__(self): def __init__(self):
self.snippets = set() # List of methods that operate only on the interpreter. self.snippets = set() # List of methods that operate only on the interpreter.

@ -30,6 +30,7 @@ from .. import interpreter
from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget
from . import find_program, get_include_args from . import find_program, get_include_args
from . import ExtensionModule from . import ExtensionModule
from . import noKwargs, permittedKwargs
# gresource compilation is broken due to the way # gresource compilation is broken due to the way
@ -90,6 +91,8 @@ class GnomeModule(ExtensionModule):
mlog.bold('https://github.com/mesonbuild/meson/issues/1387')) mlog.bold('https://github.com/mesonbuild/meson/issues/1387'))
gdbuswarning_printed = True 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): def compile_resources(self, state, args, kwargs):
self.__print_gresources_warning(state) self.__print_gresources_warning(state)
glib_version = self._get_native_glib_version(state) glib_version = self._get_native_glib_version(state)
@ -377,6 +380,10 @@ class GnomeModule(ExtensionModule):
return cflags, ldflags, gi_includes 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): def generate_gir(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException('Gir takes one argument') raise MesonException('Gir takes one argument')
@ -595,6 +602,7 @@ class GnomeModule(ExtensionModule):
rv = [scan_target, typelib_target] rv = [scan_target, typelib_target]
return ModuleReturnValue(rv, rv) return ModuleReturnValue(rv, rv)
@noKwargs
def compile_schemas(self, state, args, kwargs): def compile_schemas(self, state, args, kwargs):
if args: if args:
raise MesonException('Compile_schemas does not take positional arguments.') raise MesonException('Compile_schemas does not take positional arguments.')
@ -613,6 +621,7 @@ class GnomeModule(ExtensionModule):
target_g = build.CustomTarget(targetname, state.subdir, kwargs) target_g = build.CustomTarget(targetname, state.subdir, kwargs)
return ModuleReturnValue(target_g, [target_g]) return ModuleReturnValue(target_g, [target_g])
@permittedKwargs({'sources', 'media', 'symlink_media', 'languages'})
def yelp(self, state, args, kwargs): def yelp(self, state, args, kwargs):
if len(args) < 1: if len(args) < 1:
raise MesonException('Yelp requires a project id') raise MesonException('Yelp requires a project id')
@ -670,6 +679,10 @@ class GnomeModule(ExtensionModule):
rv = [inscript, pottarget, potarget] rv = [inscript, pottarget, potarget]
return ModuleReturnValue(None, rv) 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): def gtkdoc(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException('Gtkdoc must have one positional argument.') raise MesonException('Gtkdoc must have one positional argument.')
@ -763,6 +776,7 @@ class GnomeModule(ExtensionModule):
return args return args
@noKwargs
def gtkdoc_html_dir(self, state, args, kwargs): def gtkdoc_html_dir(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException('Must have exactly one argument.') raise MesonException('Must have exactly one argument.')
@ -792,6 +806,7 @@ class GnomeModule(ExtensionModule):
return [] return []
@permittedKwargs({'interface_prefix', 'namespace', 'object_manager'})
def gdbus_codegen(self, state, args, kwargs): def gdbus_codegen(self, state, args, kwargs):
if len(args) != 2: if len(args) != 2:
raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') 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) ct = build.CustomTarget(target_name, state.subdir, custom_kwargs)
return ModuleReturnValue(ct, [ct]) 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): def mkenums(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException('Mkenums requires one positional argument.') raise MesonException('Mkenums requires one positional argument.')
@ -932,6 +950,8 @@ class GnomeModule(ExtensionModule):
# https://github.com/mesonbuild/meson/issues/973 # https://github.com/mesonbuild/meson/issues/973
absolute_paths=True) absolute_paths=True)
@permittedKwargs({'sources', 'prefix', 'install_header', 'install_dir', 'stdinc',
'nostdinc', 'internal', 'skip_source', 'valist_marshallers'})
def genmarshal(self, state, args, kwargs): def genmarshal(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException( raise MesonException(
@ -1070,6 +1090,8 @@ class GnomeModule(ExtensionModule):
link_with += self._get_vapi_link_with(dep) link_with += self._get_vapi_link_with(dep)
return link_with return link_with
@permittedKwargs({'sources', 'packages', 'metadata_dirs', 'gir_dirs',
'vapi_dirs', 'install', 'install_dir'})
def generate_vapi(self, state, args, kwargs): def generate_vapi(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise MesonException('The library name is required') raise MesonException('The library name is required')

@ -20,6 +20,7 @@ from .. import coredata, mesonlib, build
from ..mesonlib import MesonException from ..mesonlib import MesonException
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule from . import ExtensionModule
from . import permittedKwargs
PRESET_ARGS = { PRESET_ARGS = {
'glib': [ 'glib': [
@ -55,6 +56,8 @@ class I18nModule(ExtensionModule):
src_dir = path.join(state.environment.get_source_dir(), state.subdir) src_dir = path.join(state.environment.get_source_dir(), state.subdir)
return [path.join(src_dir, d) for d in dirs] 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): def merge_file(self, state, args, kwargs):
podir = kwargs.pop('po_dir', None) podir = kwargs.pop('po_dir', None)
if not podir: if not podir:
@ -78,6 +81,7 @@ class I18nModule(ExtensionModule):
ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs) ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs)
return ModuleReturnValue(ct, [ct]) return ModuleReturnValue(ct, [ct])
@permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages'})
def gettext(self, state, args, kwargs): def gettext(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise coredata.MesonException('Gettext requires one positional argument (package name).') raise coredata.MesonException('Gettext requires one positional argument (package name).')

@ -14,9 +14,11 @@
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule from . import ExtensionModule
from . import noKwargs
class TestModule(ExtensionModule): class TestModule(ExtensionModule):
@noKwargs
def print_hello(self, state, args, kwargs): def print_hello(self, state, args, kwargs):
print('Hello from a Meson module') print('Hello from a Meson module')
rv = ModuleReturnValue(None, []) rv = ModuleReturnValue(None, [])

@ -19,6 +19,7 @@ from .. import mesonlib
from .. import mlog from .. import mlog
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule from . import ExtensionModule
from . import permittedKwargs
class PkgConfigModule(ExtensionModule): class PkgConfigModule(ExtensionModule):
@ -114,6 +115,9 @@ class PkgConfigModule(ExtensionModule):
processed_libs.append(l) processed_libs.append(l)
return processed_libs return processed_libs
@permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
'subdirs', 'requires', 'requires_private', 'libraries_private',
'install_dir', 'variables'})
def generate(self, state, args, kwargs): def generate(self, state, args, kwargs):
if len(args) > 0: if len(args) > 0:
raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.') raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.')

@ -18,6 +18,11 @@ from .. import mesonlib, dependencies
from . import ExtensionModule from . import ExtensionModule
from mesonbuild.modules import ModuleReturnValue 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): class Python3Module(ExtensionModule):
@ -25,6 +30,7 @@ class Python3Module(ExtensionModule):
super().__init__() super().__init__()
self.snippets.add('extension_module') self.snippets.add('extension_module')
@permittedSnippetKwargs(mod_kwargs)
def extension_module(self, interpreter, state, args, kwargs): def extension_module(self, interpreter, state, args, kwargs):
if 'name_prefix' in kwargs: if 'name_prefix' in kwargs:
raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.')
@ -43,20 +49,19 @@ class Python3Module(ExtensionModule):
kwargs['name_suffix'] = suffix kwargs['name_suffix'] = suffix
return interpreter.func_shared_module(None, args, kwargs) return interpreter.func_shared_module(None, args, kwargs)
@noKwargs
def find_python(self, state, args, kwargs): def find_python(self, state, args, kwargs):
py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True) py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True)
return ModuleReturnValue(py3, [py3]) return ModuleReturnValue(py3, [py3])
@noKwargs
def language_version(self, state, args, kwargs): 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(), []) return ModuleReturnValue(sysconfig.get_python_version(), [])
@noKwargs
def sysconfig_path(self, state, args, kwargs): def sysconfig_path(self, state, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise mesonlib.MesonException('sysconfig_path() requires passing the name of path to get.') 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] path_name = args[0]
valid_names = sysconfig.get_path_names() valid_names = sysconfig.get_path_names()
if path_name not in valid_names: if path_name not in valid_names:

@ -20,6 +20,7 @@ from ..dependencies import Qt4Dependency
from . import ExtensionModule from . import ExtensionModule
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue from . import ModuleReturnValue
from . import permittedKwargs
class Qt4Module(ExtensionModule): class Qt4Module(ExtensionModule):
tools_detected = False tools_detected = False
@ -96,6 +97,7 @@ class Qt4Module(ExtensionModule):
except Exception: except Exception:
return [] return []
@permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs): def preprocess(self, state, args, kwargs):
rcc_files = kwargs.pop('qresources', []) rcc_files = kwargs.pop('qresources', [])
if not isinstance(rcc_files, list): if not isinstance(rcc_files, list):

@ -20,6 +20,7 @@ from ..dependencies import Qt5Dependency
from . import ExtensionModule from . import ExtensionModule
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue from . import ModuleReturnValue
from . import permittedKwargs
class Qt5Module(ExtensionModule): class Qt5Module(ExtensionModule):
tools_detected = False tools_detected = False
@ -102,6 +103,7 @@ class Qt5Module(ExtensionModule):
except Exception: except Exception:
return [] return []
@permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs): def preprocess(self, state, args, kwargs):
rcc_files = kwargs.pop('qresources', []) rcc_files = kwargs.pop('qresources', [])
if not isinstance(rcc_files, list): if not isinstance(rcc_files, list):

@ -22,11 +22,13 @@ from .. import mlog
from . import GirTarget, TypelibTarget from . import GirTarget, TypelibTarget
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule from . import ExtensionModule
from . import noKwargs
import os import os
class RPMModule(ExtensionModule): class RPMModule(ExtensionModule):
@noKwargs
def generate_spec_template(self, state, args, kwargs): def generate_spec_template(self, state, args, kwargs):
compiler_deps = set() compiler_deps = set()
for compiler in state.compilers.values(): for compiler in state.compilers.values():

@ -20,6 +20,7 @@ from ..mesonlib import MesonException
from . import get_include_args from . import get_include_args
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule from . import ExtensionModule
from . import permittedKwargs
class WindowsModule(ExtensionModule): class WindowsModule(ExtensionModule):
@ -29,6 +30,7 @@ class WindowsModule(ExtensionModule):
return compilers[l] return compilers[l]
raise MesonException('Resource compilation requires a C or C++ compiler.') raise MesonException('Resource compilation requires a C or C++ compiler.')
@permittedKwargs({'args', 'include_directories'})
def compile_resources(self, state, args, kwargs): def compile_resources(self, state, args, kwargs):
comp = self.detect_compiler(state.compilers) comp = self.detect_compiler(state.compilers)

@ -0,0 +1,5 @@
project('module test', 'c')
modtest = import('modtest', i_cause: 'a_build_failure')
modtest.print_hello()
Loading…
Cancel
Save