diff options
-rw-r--r-- | mesonbuild/build.py | 5 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 57 | ||||
-rw-r--r-- | mesonbuild/interpreterbase.py | 26 | ||||
-rw-r--r-- | mesonbuild/mesonlib.py | 4 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 9 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 8 | ||||
-rw-r--r-- | mesonbuild/optinterpreter.py | 12 | ||||
-rwxr-xr-x | run_unittests.py | 19 |
8 files changed, 75 insertions, 65 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 360285e..d7f3b66 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -778,7 +778,7 @@ class BuildTarget(Target): if isinstance(src, str): src = File(False, self.subdir, src) elif isinstance(src, File): - FeatureNew('File argument for extract_objects', '0.50.0').use(self.subproject) + FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject) else: raise MesonException('Object extraction arguments must be strings or Files.') # FIXME: It could be a generated source @@ -2162,7 +2162,7 @@ class CustomTarget(Target): 'when installing a target') if isinstance(kwargs['install_dir'], list): - FeatureNew('multiple install_dir for custom_target', '0.40.0').use(self.subproject) + FeatureNew.single_use('multiple install_dir for custom_target', '0.40.0', self.subproject) # If an item in this list is False, the output corresponding to # the list index of that item will not be installed self.install_dir = typeslistify(kwargs['install_dir'], (str, bool)) @@ -2174,7 +2174,6 @@ class CustomTarget(Target): if 'build_always' in kwargs and 'build_always_stale' in kwargs: raise InvalidArguments('build_always and build_always_stale are mutually exclusive. Combine build_by_default and build_always_stale.') elif 'build_always' in kwargs: - mlog.deprecation('build_always is deprecated. Combine build_by_default and build_always_stale instead.') if 'build_by_default' not in kwargs: self.build_by_default = kwargs['build_always'] self.build_always_stale = kwargs['build_always'] diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 8c7a82c..07ab4f0 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -29,7 +29,7 @@ from .interpreterbase import InterpreterBase from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound -from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs +from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs from .interpreterbase import ObjectHolder from .modules import ModuleReturnValue from .cmake import CMakeInterpreter @@ -527,8 +527,9 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): @noPosargs @permittedKwargs({}) + @FeatureDeprecated('ExternalProgram.path', '0.55.0', + 'use ExternalProgram.full_path() instead') def path_method(self, args, kwargs): - mlog.deprecation('path() method is deprecated and replaced by full_path()') return self._full_path() @noPosargs @@ -1957,8 +1958,10 @@ class MesonMain(InterpreterObject): 'Arguments to {} must be strings, Files, CustomTargets, ' 'Indexes of CustomTargets, or ConfigureFiles'.format(name)) if new: - FeatureNew('Calling "{}" with File, CustomTaget, Index of CustomTarget, ConfigureFile, Executable, or ExternalProgram'.format(name), '0.55.0').use( - self.interpreter.subproject) + FeatureNew.single_use( + 'Calling "{}" with File, CustomTaget, Index of CustomTarget, ' + 'ConfigureFile, Executable, or ExternalProgram'.format(name), + '0.55.0', self.interpreter.subproject) return script_args @permittedKwargs(set()) @@ -1982,7 +1985,8 @@ class MesonMain(InterpreterObject): if len(args) < 1: raise InterpreterException('add_dist_script takes one or more arguments') if len(args) > 1: - FeatureNew('Calling "add_dist_script" with multiple arguments', '0.49.0').use(self.interpreter.subproject) + FeatureNew.single_use('Calling "add_dist_script" with multiple arguments', + '0.49.0', self.interpreter.subproject) if self.interpreter.subproject != '': raise InterpreterException('add_dist_script may not be used in a subproject.') script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True) @@ -2605,7 +2609,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_assert(self, node, args, kwargs): if len(args) == 1: - FeatureNew('assert function without message argument', '0.53.0').use(self.subproject) + FeatureNew.single_use('assert function without message argument', '0.53.0', self.subproject) value = args[0] message = None elif len(args) == 2: @@ -2941,7 +2945,7 @@ external dependencies (including libraries) must go to "dependencies".''') if len(args) > 1: raise InterpreterException('configuration_data takes only one optional positional arguments') elif len(args) == 1: - FeatureNew('configuration_data dictionary', '0.49.0').use(self.subproject) + FeatureNew.single_use('configuration_data dictionary', '0.49.0', self.subproject) initial_values = args[0] if not isinstance(initial_values, dict): raise InterpreterException('configuration_data first argument must be a dictionary') @@ -2981,11 +2985,14 @@ external dependencies (including libraries) must go to "dependencies".''') if ':' in proj_name: raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name)) + # This needs to be evaluated as early as possible, as meson uses this + # for things like deprecation testing. if 'meson_version' in kwargs: cv = coredata.version pv = kwargs['meson_version'] if not mesonlib.version_compare(cv, pv): raise InterpreterException('Meson version is %s but project requires %s' % (cv, pv)) + mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] if os.path.exists(self.option_file): oi = optinterpreter.OptionInterpreter(self.subproject) @@ -3032,10 +3039,6 @@ external dependencies (including libraries) must go to "dependencies".''') self.build.subproject_dir = self.subproject_dir - mesonlib.project_meson_versions[self.subproject] = '' - if 'meson_version' in kwargs: - mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] - self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) @@ -3082,7 +3085,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_message(self, node, args, kwargs): if len(args) > 1: - FeatureNew('message with more than one argument', '0.54.0').use(self.subproject) + FeatureNew.single_use('message with more than one argument', '0.54.0', self.subproject) args_str = [self.get_message_string_arg(i) for i in args] self.message_impl(args_str) @@ -3144,7 +3147,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_warning(self, node, args, kwargs): if len(args) > 1: - FeatureNew('warning with more than one argument', '0.54.0').use(self.subproject) + FeatureNew.single_use('warning with more than one argument', '0.54.0', self.subproject) args_str = [self.get_message_string_arg(i) for i in args] mlog.warning(*args_str, location=node) @@ -3468,15 +3471,15 @@ external dependencies (including libraries) must go to "dependencies".''') def _handle_featurenew_dependencies(self, name): 'Do a feature check on dependencies used by this subproject' if name == 'mpi': - FeatureNew('MPI Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('MPI Dependency', '0.42.0', self.subproject) elif name == 'pcap': - FeatureNew('Pcap Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('Pcap Dependency', '0.42.0', self.subproject) elif name == 'vulkan': - FeatureNew('Vulkan Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('Vulkan Dependency', '0.42.0', self.subproject) elif name == 'libwmf': - FeatureNew('LibWMF Dependency', '0.44.0').use(self.subproject) + FeatureNew.single_use('LibWMF Dependency', '0.44.0', self.subproject) elif name == 'openmp': - FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject) + FeatureNew.single_use('OpenMP Dependency', '0.46.0', self.subproject) @FeatureNewKwargs('dependency', '0.54.0', ['components']) @FeatureNewKwargs('dependency', '0.52.0', ['include_type']) @@ -3598,7 +3601,7 @@ external dependencies (including libraries) must go to "dependencies".''') def get_subproject_infos(self, kwargs): fbinfo = mesonlib.stringlistify(kwargs['fallback']) if len(fbinfo) == 1: - FeatureNew('Fallback without variable name', '0.53.0').use(self.subproject) + FeatureNew.single_use('Fallback without variable name', '0.53.0', self.subproject) return fbinfo[0], None elif len(fbinfo) != 2: raise InterpreterException('Fallback info must have one or two items.') @@ -3686,11 +3689,13 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Unknown target_type.') @permittedKwargs(permitted_kwargs['vcs_tag']) + @FeatureDeprecatedKwargs('custom_target', '0.47.0', ['build_always'], + 'combine build_by_default and build_always_stale instead.') 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') if 'fallback' not in kwargs: - FeatureNew('Optional fallback in vcs_tag', '0.41.0').use(self.subproject) + FeatureNew.single_use('Optional fallback in vcs_tag', '0.41.0', self.subproject) fallback = kwargs.pop('fallback', self.project_version) if not isinstance(fallback, str): raise InterpreterException('Keyword argument fallback must be a string.') @@ -3743,7 +3748,7 @@ external dependencies (including libraries) must go to "dependencies".''') if len(args) != 1: raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name') if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']): - FeatureNew('substitutions in custom_target depfile', '0.47.0').use(self.subproject) + FeatureNew.single_use('substitutions in custom_target depfile', '0.47.0', self.subproject) return self._func_custom_target_impl(node, args, kwargs) def _func_custom_target_impl(self, node, args, kwargs): @@ -3832,7 +3837,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self @permittedKwargs(permitted_kwargs['test']) def func_test(self, node, args, kwargs): if kwargs.get('protocol') == 'gtest': - FeatureNew('"gtest" protocol for tests', '0.55.0').use(self.subproject) + FeatureNew.single_use('"gtest" protocol for tests', '0.55.0', self.subproject) self.add_test(node, args, kwargs, True) def unpack_env_kwarg(self, kwargs) -> build.EnvironmentVariables: @@ -3840,7 +3845,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if isinstance(envlist, EnvironmentVariablesHolder): env = envlist.held_object elif isinstance(envlist, dict): - FeatureNew('environment dictionary', '0.52.0').use(self.subproject) + FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject) env = EnvironmentVariablesHolder(envlist) env = env.held_object else: @@ -4156,7 +4161,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if 'configuration' in kwargs: conf = kwargs['configuration'] if isinstance(conf, dict): - FeatureNew('configure_file.configuration dictionary', '0.49.0').use(self.subproject) + FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject) conf = ConfigurationDataHolder(self.subproject, conf) elif not isinstance(conf, ConfigurationDataHolder): raise InterpreterException('Argument "configuration" is not of type configuration_data') @@ -4186,7 +4191,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self conf.mark_used() elif 'command' in kwargs: if len(inputs) > 1: - FeatureNew('multiple inputs in configure_file()', '0.52.0').use(self.subproject) + FeatureNew.single_use('multiple inputs in configure_file()', '0.52.0', self.subproject) # We use absolute paths for input and output here because the cwd # that the command is run from is 'unspecified', so it could change. # Currently it's builddir/subdir for in_builddir else srcdir/subdir. @@ -4434,7 +4439,7 @@ different subdirectory. if len(args) > 1: raise InterpreterException('environment takes only one optional positional arguments') elif len(args) == 1: - FeatureNew('environment positional arguments', '0.52.0').use(self.subproject) + FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject) initial_values = args[0] if not isinstance(initial_values, dict) and not isinstance(initial_values, list): raise InterpreterException('environment first argument must be a dictionary or a list') diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index af9018b..634f4f2 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -286,6 +286,13 @@ class FeatureCheckBase(metaclass=abc.ABCMeta): return f(*wrapped_args, **wrapped_kwargs) return wrapped + @classmethod + def single_use(cls, feature_name: str, version: str, subproject: str, + extra_message: T.Optional[str] = None) -> None: + """Oneline version that instantiates and calls use().""" + cls(feature_name, version, extra_message).use(subproject) + + class FeatureNew(FeatureCheckBase): """Checks for new features""" @@ -342,17 +349,23 @@ class FeatureDeprecated(FeatureCheckBase): mlog.warning(*args) -class FeatureCheckKwargsBase: - def __init__(self, feature_name: str, feature_version: str, kwargs: T.List[str]): +class FeatureCheckKwargsBase(metaclass=abc.ABCMeta): + + @property + @abc.abstractmethod + def feature_check_class(self) -> T.Type[FeatureCheckBase]: + pass + + def __init__(self, feature_name: str, feature_version: str, + kwargs: T.List[str], extra_message: T.Optional[str] = None): self.feature_name = feature_name self.feature_version = feature_version self.kwargs = kwargs + self.extra_message = extra_message def __call__(self, f): @wraps(f) def wrapped(*wrapped_args, **wrapped_kwargs): - # Which FeatureCheck class to invoke - FeatureCheckClass = self.feature_check_class kwargs, subproject = _get_callee_args(wrapped_args, want_subproject=True)[3:5] if subproject is None: raise AssertionError('{!r}'.format(wrapped_args)) @@ -360,7 +373,8 @@ class FeatureCheckKwargsBase: if arg not in kwargs: continue name = arg + ' arg in ' + self.feature_name - FeatureCheckClass(name, self.feature_version).use(subproject) + self.feature_check_class.single_use( + name, self.feature_version, subproject, self.extra_message) return f(*wrapped_args, **wrapped_kwargs) return wrapped @@ -570,7 +584,7 @@ class InterpreterBase: self.argument_depth += 1 for key, value in kwargs.items(): if not isinstance(key, mparser.StringNode): - FeatureNew('Dictionary entry using non literal key', '0.53.0').use(self.subproject) + FeatureNew.single_use('Dictionary entry using non literal key', '0.53.0', self.subproject) assert isinstance(key, mparser.BaseNode) # All keys must be nodes due to resolve_key_nodes=False str_key = self.evaluate_statement(key) if not isinstance(str_key, str): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index b901ec9..26fe6eb 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -39,8 +39,10 @@ _U = T.TypeVar('_U') have_fcntl = False have_msvcrt = False +# TODO: this is such a hack, this really should be either in coredata or in the +# interpreter # {subproject: project_meson_version} -project_meson_versions = {} # type: T.Dict[str, str] +project_meson_versions = collections.defaultdict(str) # type: T.DefaultDict[str, str] try: import fcntl diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 01acb37..ea1b325 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -33,7 +33,7 @@ from ..mesonlib import ( join_args, unholder, ) from ..dependencies import Dependency, PkgConfigDependency, InternalDependency, ExternalProgram -from ..interpreterbase import noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs +from ..interpreterbase import noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs, FeatureDeprecatedKwargs # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it @@ -834,6 +834,8 @@ class GnomeModule(ExtensionModule): return ModuleReturnValue(target_g, [target_g]) @permittedKwargs({'sources', 'media', 'symlink_media', 'languages'}) + @FeatureDeprecatedKwargs('gnome.yelp', '0.43.0', ['languages'], + 'Use a LINGUAS file in the source directory instead') def yelp(self, state, args, kwargs): if len(args) < 1: raise MesonException('Yelp requires a project id') @@ -848,11 +850,6 @@ class GnomeModule(ExtensionModule): source_str = '@@'.join(sources) langs = mesonlib.stringlistify(kwargs.pop('languages', [])) - if langs: - mlog.deprecation('''The "languages" argument of gnome.yelp() is deprecated. -Use a LINGUAS file in the sources directory instead. -This will become a hard error in the future.''') - media = mesonlib.stringlistify(kwargs.pop('media', [])) symlinks = kwargs.pop('symlink_media', True) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 666a93d..7597eeb 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -76,7 +76,7 @@ class DependenciesHelper: processed_reqs = [] for obj in mesonlib.unholder(mesonlib.listify(reqs)): if not isinstance(obj, str): - FeatureNew('pkgconfig.generate requirement from non-string object', '0.46.0').use(self.state.subproject) + FeatureNew.single_use('pkgconfig.generate requirement from non-string object', '0.46.0', self.state.subproject) if hasattr(obj, 'generated_pc'): self._check_generated_pc_deprecation(obj) processed_reqs.append(obj.generated_pc) @@ -394,8 +394,6 @@ class PkgConfigModule(ExtensionModule): 'install_dir', 'extra_cflags', 'variables', 'url', 'd_module_versions', 'dataonly', 'conflicts'}) def generate(self, state, args, kwargs): - if 'variables' in kwargs: - FeatureNew('custom pkgconfig variables', '0.41.0').use(state.subproject) default_version = state.project_version['version'] default_install_dir = None default_description = None @@ -403,9 +401,9 @@ class PkgConfigModule(ExtensionModule): mainlib = None default_subdirs = ['.'] if not args and 'version' not in kwargs: - FeatureNew('pkgconfig.generate implicit version keyword', '0.46.0').use(state.subproject) + FeatureNew.single_use('pkgconfig.generate implicit version keyword', '0.46.0', state.subproject) elif len(args) == 1: - FeatureNew('pkgconfig.generate optional positional argument', '0.46.0').use(state.subproject) + FeatureNew.single_use('pkgconfig.generate optional positional argument', '0.46.0', state.subproject) mainlib = getattr(args[0], 'held_object', args[0]) if not isinstance(mainlib, (build.StaticLibrary, build.SharedLibrary)): raise mesonlib.MesonException('Pkgconfig_gen first positional argument must be a library object') diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index c13cc5d..dfbe6d7 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -16,10 +16,11 @@ import re import functools import typing as T -from . import mparser +from . import compilers from . import coredata from . import mesonlib -from . import compilers +from . import mparser +from .interpreterbase import FeatureNew forbidden_option_names = set(coredata.builtin_options.keys()) forbidden_prefixes = [lang + '_' for lang in compilers.all_languages] + ['b_', 'backend_'] @@ -200,11 +201,8 @@ class OptionInterpreter: raise OptionException('Only calls to option() are allowed in option files.') (posargs, kwargs) = self.reduce_arguments(node.args) - # FIXME: Cannot use FeatureNew while parsing options because we parse - # it before reading options in project(). See func_project() in - # interpreter.py - #if 'yield' in kwargs: - # FeatureNew('option yield', '0.45.0').use(self.subproject) + if 'yield' in kwargs: + FeatureNew.single_use('option yield', '0.45.0', self.subproject) if 'type' not in kwargs: raise OptionException('Option call missing mandatory "type" keyword argument') diff --git a/run_unittests.py b/run_unittests.py index 7e5dffc..651e366 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -718,25 +718,22 @@ class InternalTests(unittest.TestCase): self.assertEqual([1, 2, 3], extract(kwargs, 'sources')) def test_pkgconfig_module(self): - - class Mock: - pass - - dummystate = Mock() + dummystate = mock.Mock() dummystate.subproject = 'dummy' - mock = Mock() - mock.pcdep = Mock() - mock.pcdep.name = "some_name" - mock.version_reqs = [] + _mock = mock.Mock(spec=mesonbuild.dependencies.ExternalDependency) + _mock.pcdep = mock.Mock() + _mock.pcdep.name = "some_name" + _mock.version_reqs = [] + _mock = mock.Mock(held_object=_mock) # pkgconfig dependency as lib deps = mesonbuild.modules.pkgconfig.DependenciesHelper(dummystate, "thislib") - deps.add_pub_libs([mock]) + deps.add_pub_libs([_mock]) self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name") # pkgconfig dependency as requires deps = mesonbuild.modules.pkgconfig.DependenciesHelper(dummystate, "thislib") - deps.add_pub_reqs([mock]) + deps.add_pub_reqs([_mock]) self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name") def _test_all_naming(self, cc, env, patterns, platform): |