aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/build.py5
-rw-r--r--mesonbuild/interpreter.py57
-rw-r--r--mesonbuild/interpreterbase.py26
-rw-r--r--mesonbuild/mesonlib.py4
-rw-r--r--mesonbuild/modules/gnome.py9
-rw-r--r--mesonbuild/modules/pkgconfig.py8
-rw-r--r--mesonbuild/optinterpreter.py12
-rwxr-xr-xrun_unittests.py19
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):