aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/interpreter.py118
-rw-r--r--mesonbuild/modules/__init__.py51
-rw-r--r--mesonbuild/modules/fs.py3
-rw-r--r--mesonbuild/modules/python.py13
-rw-r--r--mesonbuild/modules/unstable_rust.py3
5 files changed, 85 insertions, 103 deletions
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 570b34f..2cc4f44 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -33,7 +33,7 @@ from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, Fe
from .interpreterbase import ObjectHolder, MesonVersionString
from .interpreterbase import TYPE_var, TYPE_nkwargs
from .interpreterbase import typed_pos_args
-from .modules import ModuleReturnValue, ExtensionModule
+from .modules import ModuleReturnValue, ModuleObject, ModuleState
from .cmake import CMakeInterpreter
from .backend.backends import TestProtocol, Backend, ExecutableSerialisation
@@ -55,7 +55,6 @@ if T.TYPE_CHECKING:
from .compilers import Compiler
from .envconfig import MachineInfo
from .environment import Environment
- from .modules import ExtensionModule
permitted_method_kwargs = {
'partial_dependency': {'compile_args', 'link_args', 'links', 'includes',
@@ -1765,93 +1764,43 @@ class CompilerHolder(InterpreterObject):
return self.compiler.get_argument_syntax()
-class ModuleState(T.NamedTuple):
-
- """Object passed to a module when it a method is called.
-
- holds the current state of the meson process at a given method call in
- the interpreter.
- """
-
- source_root: str
- build_to_src: str
- subproject: str
- subdir: str
- current_lineno: str
- environment: 'Environment'
- project_name: str
- project_version: str
- backend: str
- targets: T.Dict[str, build.Target]
- data: T.List[build.Data]
- headers: T.List[build.Headers]
- man: T.List[build.Man]
- global_args: T.Dict[str, T.List[str]]
- project_args: T.Dict[str, T.List[str]]
- build_machine: 'MachineInfo'
- host_machine: 'MachineInfo'
- target_machine: 'MachineInfo'
- current_node: mparser.BaseNode
-
-
-class ModuleHolder(InterpreterObject, ObjectHolder['ExtensionModule']):
- def __init__(self, modname: str, module: 'ExtensionModule', interpreter: 'Interpreter'):
+class ModuleObjectHolder(InterpreterObject, ObjectHolder['ModuleObject']):
+ def __init__(self, modobj: 'ModuleObject', interpreter: 'Interpreter'):
InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, module)
- self.modname = modname
+ ObjectHolder.__init__(self, modobj)
self.interpreter = interpreter
def method_call(self, method_name, args, kwargs):
- try:
- fn = getattr(self.held_object, method_name)
- except AttributeError:
- raise InvalidArguments('Module %s does not have method %s.' % (self.modname, method_name))
- if method_name.startswith('_'):
- raise InvalidArguments('Function {!r} in module {!r} is private.'.format(method_name, self.modname))
- if not getattr(fn, 'no-args-flattening', False):
+ modobj = self.held_object
+ method = modobj.methods.get(method_name)
+ if not method and not modobj.methods:
+ # FIXME: Port all modules to use the methods dict.
+ method = getattr(modobj, method_name, None)
+ if method_name.startswith('_'):
+ raise InvalidArguments('Method {!r} is private.'.format(method_name))
+ if not method:
+ raise InvalidCode('Unknown method "%s" in object.' % method_name)
+ if not getattr(method, 'no-args-flattening', False):
args = flatten(args)
- # This is not 100% reliable but we can't use hash()
- # because the Build object contains dicts and lists.
- num_targets = len(self.interpreter.build.targets)
- state = ModuleState(
- source_root = self.interpreter.environment.get_source_dir(),
- build_to_src=mesonlib.relpath(self.interpreter.environment.get_source_dir(),
- self.interpreter.environment.get_build_dir()),
- subproject=self.interpreter.subproject,
- subdir=self.interpreter.subdir,
- current_lineno=self.interpreter.current_lineno,
- environment=self.interpreter.environment,
- project_name=self.interpreter.build.project_name,
- project_version=self.interpreter.build.dep_manifest[self.interpreter.active_projectname],
- # The backend object is under-used right now, but we will need it:
- # https://github.com/mesonbuild/meson/issues/1419
- backend=self.interpreter.backend,
- targets=self.interpreter.build.targets,
- data=self.interpreter.build.data,
- headers=self.interpreter.build.get_headers(),
- man=self.interpreter.build.get_man(),
- #global_args_for_build = self.interpreter.build.global_args.build,
- global_args = self.interpreter.build.global_args.host,
- #project_args_for_build = self.interpreter.build.projects_args.build.get(self.interpreter.subproject, {}),
- project_args = self.interpreter.build.projects_args.host.get(self.interpreter.subproject, {}),
- build_machine=self.interpreter.builtin['build_machine'].held_object,
- host_machine=self.interpreter.builtin['host_machine'].held_object,
- target_machine=self.interpreter.builtin['target_machine'].held_object,
- current_node=self.current_node
- )
+ state = ModuleState(self.interpreter)
# Many modules do for example self.interpreter.find_program_impl(),
# so we have to ensure they use the current interpreter and not the one
# that first imported that module, otherwise it will use outdated
# overrides.
- self.held_object.interpreter = self.interpreter
- if self.held_object.is_snippet(method_name):
- value = fn(self.interpreter, state, args, kwargs)
- return self.interpreter.holderify(value)
+ modobj.interpreter = self.interpreter
+ if method_name in modobj.snippets:
+ ret = method(self.interpreter, state, args, kwargs)
else:
- value = fn(state, args, kwargs)
+ # This is not 100% reliable but we can't use hash()
+ # because the Build object contains dicts and lists.
+ num_targets = len(self.interpreter.build.targets)
+ ret = method(state, args, kwargs)
if num_targets != len(self.interpreter.build.targets):
raise InterpreterException('Extension module altered internal state illegally.')
- return self.interpreter.module_method_callback(value)
+ if isinstance(ret, ModuleReturnValue):
+ self.interpreter.process_new_values(ret.new_objects)
+ ret = ret.return_value
+ return self.interpreter.holderify(ret)
class Summary:
@@ -2401,7 +2350,7 @@ class Interpreter(InterpreterBase):
subproject: str = '',
subdir: str = '',
subproject_dir: str = 'subprojects',
- modules: T.Optional[T.Dict[str, ExtensionModule]] = None,
+ modules: T.Optional[T.Dict[str, ModuleObject]] = None,
default_project_options: T.Optional[T.Dict[str, str]] = None,
mock: bool = False,
ast: T.Optional[mparser.CodeBlockNode] = None,
@@ -2566,6 +2515,8 @@ class Interpreter(InterpreterBase):
return DependencyHolder(item, self.subproject)
elif isinstance(item, dependencies.ExternalProgram):
return ExternalProgramHolder(item, self.subproject)
+ elif isinstance(item, ModuleObject):
+ return ModuleObjectHolder(item, self)
elif isinstance(item, (InterpreterObject, ObjectHolder)):
return item
else:
@@ -2600,13 +2551,6 @@ class Interpreter(InterpreterBase):
else:
raise InterpreterException('Module returned a value of unknown type.')
- def module_method_callback(self, return_object):
- if not isinstance(return_object, ModuleReturnValue):
- raise InterpreterException('Bug in module, it returned an invalid object')
- invalues = return_object.new_objects
- self.process_new_values(invalues)
- return self.holderify(return_object.return_value)
-
def get_build_def_files(self) -> T.List[str]:
return self.build_def_files
@@ -2676,7 +2620,7 @@ class Interpreter(InterpreterBase):
except ImportError:
raise InvalidArguments('Module "%s" does not exist' % (modname, ))
ext_module = module.initialize(self)
- assert isinstance(ext_module, ExtensionModule)
+ assert isinstance(ext_module, ModuleObject)
self.modules[modname] = ext_module
@stringArgs
@@ -2696,7 +2640,7 @@ class Interpreter(InterpreterBase):
mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
modname = 'unstable_' + plainname
self.import_module(modname)
- return ModuleHolder(modname, self.modules[modname], self)
+ return ModuleObjectHolder(self.modules[modname], self)
@stringArgs
@noKwargs
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index ff27a11..1cf7c1c 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -18,21 +18,58 @@
import os
from .. import build
-from ..mesonlib import unholder
+from ..mesonlib import unholder, relpath
import typing as T
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
- from ..interpreterbase import TYPE_var
+ from ..interpreterbase import TYPE_var, TYPE_nvar, TYPE_nkwargs
+
+class ModuleState:
+ """Object passed to all module methods.
+
+ This is a WIP API provided to modules, it should be extended to have everything
+ needed so modules does not touch any other part of Meson internal APIs.
+ """
-class ExtensionModule:
def __init__(self, interpreter: 'Interpreter') -> None:
+ self.source_root = interpreter.environment.get_source_dir()
+ self.build_to_src = relpath(interpreter.environment.get_source_dir(),
+ interpreter.environment.get_build_dir())
+ self.subproject = interpreter.subproject
+ self.subdir = interpreter.subdir
+ self.current_lineno = interpreter.current_lineno
+ self.environment = interpreter.environment
+ self.project_name = interpreter.build.project_name
+ self.project_version = interpreter.build.dep_manifest[interpreter.active_projectname]
+ # The backend object is under-used right now, but we will need it:
+ # https://github.com/mesonbuild/meson/issues/1419
+ self.backend = interpreter.backend
+ self.targets = interpreter.build.targets
+ self.data = interpreter.build.data
+ self.headers = interpreter.build.get_headers()
+ self.man = interpreter.build.get_man()
+ self.global_args = interpreter.build.global_args.host
+ self.project_args = interpreter.build.projects_args.host.get(interpreter.subproject, {})
+ self.build_machine = interpreter.builtin['build_machine'].held_object
+ self.host_machine = interpreter.builtin['host_machine'].held_object
+ self.target_machine = interpreter.builtin['target_machine'].held_object
+ self.current_node = interpreter.current_node
+
+class ModuleObject:
+ """Base class for all objects returned by modules
+ """
+ def __init__(self, interpreter: T.Optional['Interpreter'] = None) -> None:
+ self.methods = {} # type: T.Dict[str, T.Callable[[T.List[TYPE_nvar], TYPE_nkwargs], TYPE_var]]
+ # FIXME: Port all modules to stop using self.interpreter and use API on
+ # ModuleState instead.
self.interpreter = interpreter
- self.snippets = set() # type: T.Set[str] # List of methods that operate only on the interpreter.
-
- def is_snippet(self, funcname: str) -> bool:
- return funcname in self.snippets
+ # FIXME: Port all modules to remove snippets methods.
+ self.snippets: T.Set[str] = set()
+# FIXME: Port all modules to use ModuleObject directly.
+class ExtensionModule(ModuleObject):
+ pass
def get_include_args(include_dirs, prefix='-I'):
'''
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index d0b5e97..7de8cf7 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -29,7 +29,8 @@ from ..mesonlib import (
from ..interpreterbase import FeatureNew, typed_pos_args, noKwargs, permittedKwargs
if T.TYPE_CHECKING:
- from ..interpreter import Interpreter, ModuleState
+ from . import ModuleState
+ from ..interpreter import Interpreter
class FSModule(ExtensionModule):
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 564d181..cfe2244 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -21,7 +21,6 @@ from pathlib import Path
from .. import mesonlib
from ..mesonlib import MachineChoice, MesonException
from . import ExtensionModule
-from mesonbuild.modules import ModuleReturnValue
from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs,
InvalidArguments,
@@ -399,12 +398,12 @@ class PythonInstallation(ExternalProgramHolder):
else:
res = os.path.join(self.platlib_install_path, subdir)
- return self.interpreter.module_method_callback(ModuleReturnValue(res, []))
+ return res
@noPosargs
@noKwargs
def language_version_method(self, args, kwargs):
- return self.interpreter.module_method_callback(ModuleReturnValue(self.version, []))
+ return self.version
@noKwargs
def has_path_method(self, args, kwargs):
@@ -414,7 +413,7 @@ class PythonInstallation(ExternalProgramHolder):
if not isinstance(path_name, str):
raise InvalidArguments('has_path argument must be a string.')
- return self.interpreter.module_method_callback(ModuleReturnValue(path_name in self.paths, []))
+ return path_name in self.paths
@noKwargs
def get_path_method(self, args, kwargs):
@@ -432,7 +431,7 @@ class PythonInstallation(ExternalProgramHolder):
else:
raise InvalidArguments('{} is not a valid path name'.format(path_name))
- return self.interpreter.module_method_callback(ModuleReturnValue(path, []))
+ return path
@noKwargs
def has_variable_method(self, args, kwargs):
@@ -442,7 +441,7 @@ class PythonInstallation(ExternalProgramHolder):
if not isinstance(var_name, str):
raise InvalidArguments('has_variable argument must be a string.')
- return self.interpreter.module_method_callback(ModuleReturnValue(var_name in self.variables, []))
+ return var_name in self.variables
@noKwargs
def get_variable_method(self, args, kwargs):
@@ -460,7 +459,7 @@ class PythonInstallation(ExternalProgramHolder):
else:
raise InvalidArguments('{} is not a valid variable name'.format(var_name))
- return self.interpreter.module_method_callback(ModuleReturnValue(var, []))
+ return var
@noPosargs
@noKwargs
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py
index c4d7d41..11b4365 100644
--- a/mesonbuild/modules/unstable_rust.py
+++ b/mesonbuild/modules/unstable_rust.py
@@ -24,7 +24,8 @@ from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew,
from ..mesonlib import stringlistify, unholder, listify, typeslistify, File
if T.TYPE_CHECKING:
- from ..interpreter import ModuleState, Interpreter
+ from . import ModuleState
+ from ..interpreter import Interpreter
from ..dependencies import ExternalProgram