aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-06-30 13:03:54 -0700
committerDylan Baker <dylan@pnwbakers.com>2021-06-30 16:28:14 -0700
commitbc4201a7f1e93d6afb62caa8ce81bb1b0211b70c (patch)
treee630b202d1e35803a87cb1306517f79d91dbe0de
parent4a0a6a80837af1b75db3b3b57a5b8f41386e5c0b (diff)
downloadmeson-bc4201a7f1e93d6afb62caa8ce81bb1b0211b70c.zip
meson-bc4201a7f1e93d6afb62caa8ce81bb1b0211b70c.tar.gz
meson-bc4201a7f1e93d6afb62caa8ce81bb1b0211b70c.tar.bz2
interpreter: add required and disabled to import
This is useful both from the perspective of optional functionality that requires a module, and also as I continue to progress with Meson++, which will probably not implement all of the modules that Meson itself does.
-rw-r--r--docs/markdown/Reference-manual.md20
-rw-r--r--docs/markdown/snippets/required_and_disabled_import.md5
-rw-r--r--mesonbuild/interpreter/interpreter.py59
-rw-r--r--mesonbuild/interpreter/kwargs.py5
-rw-r--r--mesonbuild/modules/__init__.py28
-rw-r--r--test cases/common/67 modules/meson.build10
-rw-r--r--test cases/common/67 modules/meson_options.txt6
-rw-r--r--test cases/failing/57 kwarg in module/meson.build5
-rw-r--r--test cases/failing/57 kwarg in module/test.json7
9 files changed, 96 insertions, 49 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 16fcbbf..e96a6fc 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -973,19 +973,21 @@ a non-existing variable will cause a fatal error.
### import()
-``` meson
- module_object import(module_name)
+```
+ module_object import(string, required : bool | feature, disabler : bool)
```
-Imports the given extension module. Returns an opaque object that can
-be used to call the methods of the module. Here's an example for a
-hypothetical `testmod` module.
+Imports the given extension module. Returns an object that can be used to call
+the methods of the module. Here's an example for a hypothetical `testmod`
+module.
```meson
tmod = import('testmod')
tmod.do_something()
```
+*Since 0.59.0* the required and disabler keyword arguments
+
### include_directories()
``` meson
@@ -2911,3 +2913,11 @@ sample piece of code with [`compiler.run()`](#compiler-object) or
- `returncode()`: the return code of executing the compiled binary
- `stderr()`: the standard error produced when the command was run
- `stdout()`: the standard out produced when the command was run
+
+### `module` object
+
+Modules provide their own specific implementation methods, but all modules
+proivide the following methods:
+
+- `bool found()`: returns True if the module was successfully imported,
+ otherwise false. *Since 0.59.0*
diff --git a/docs/markdown/snippets/required_and_disabled_import.md b/docs/markdown/snippets/required_and_disabled_import.md
new file mode 100644
index 0000000..39ca307
--- /dev/null
+++ b/docs/markdown/snippets/required_and_disabled_import.md
@@ -0,0 +1,5 @@
+## The `import()` function gains `required` and `disabler` arguments
+
+In addition, modules now have a `found()` method, like programs and
+dependencies. This allows them to be conditionally required, and used in most
+places that an object with a `found()` method can be.
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index c103f7e..9bc7bd5 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -32,7 +32,7 @@ from ..interpreterbase import Disabler, disablerIfNotFound
from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs
from ..interpreterbase import ObjectHolder, RangeHolder
from ..interpreterbase import TYPE_nkwargs, TYPE_nvar, TYPE_var
-from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule
+from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule
from ..cmake import CMakeInterpreter
from ..backend.backends import Backend, ExecutableSerialisation
@@ -161,6 +161,13 @@ _INSTALL_MODE_KW = KwargInfo(
convertor=_install_mode_convertor,
)
+_REQUIRED_KW = KwargInfo(
+ 'required',
+ (bool, coredata.UserFeatureOption),
+ default=True,
+ # TODO: extract_required_kwarg could be converted to a convertor
+)
+
def stringifyUserArguments(args, quote=False):
if isinstance(args, list):
@@ -304,7 +311,7 @@ class Interpreter(InterpreterBase, HoldableObject):
subproject: str = '',
subdir: str = '',
subproject_dir: str = 'subprojects',
- modules: T.Optional[T.Dict[str, T.Union[ExtensionModule, NewExtensionModule]]] = None,
+ modules: T.Optional[T.Dict[str, T.Union[ExtensionModule, NewExtensionModule, NotFoundExtensionModule]]] = None,
default_project_options: T.Optional[T.Dict[str, str]] = None,
mock: bool = False,
ast: T.Optional[mparser.CodeBlockNode] = None,
@@ -601,33 +608,47 @@ class Interpreter(InterpreterBase, HoldableObject):
dep = df.lookup(kwargs, force_fallback=True)
self.build.stdlibs[for_machine][l] = dep
+ def _import_module(self, modname: str, required: bool) -> T.Union[ExtensionModule, NewExtensionModule, NotFoundExtensionModule]:
+ if modname in self.modules:
+ return self.modules[modname]
+ try:
+ module = importlib.import_module('mesonbuild.modules.' + modname)
+ except ImportError:
+ if required:
+ raise InvalidArguments(f'Module "{modname}" does not exist')
+ ext_module = NotFoundExtensionModule()
+ else:
+ ext_module = module.initialize(self)
+ assert isinstance(ext_module, (ExtensionModule, NewExtensionModule))
+ self.modules[modname] = ext_module
+ return ext_module
+
@typed_pos_args('import', str)
- @noKwargs
- def func_import(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs) -> ModuleObject:
+ @typed_kwargs(
+ 'import',
+ _REQUIRED_KW.evolve(since='0.59.0'),
+ KwargInfo('disabler', bool, default=False, since='0.59.0'),
+ )
+ @disablerIfNotFound
+ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str],
+ kwargs: 'kwargs.FuncImportModule') -> T.Union[ExtensionModule, NewExtensionModule, NotFoundExtensionModule]:
modname = args[0]
+ disabled, required, _ = extract_required_kwarg(kwargs, self.subproject)
+ if disabled:
+ return NotFoundExtensionModule()
+
if modname.startswith('unstable-'):
plainname = modname.split('-', 1)[1]
try:
# check if stable module exists
- self._import_module(plainname)
+ mod = self._import_module(plainname, required)
+ # XXX: this is acutally not helpful, since it doesn't do a version check
mlog.warning(f'Module {modname} is now stable, please use the {plainname} module instead.')
- modname = plainname
+ return mod
except InvalidArguments:
mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
modname = 'unstable_' + plainname
-
- if modname in self.modules:
- return self.modules[modname]
-
- try:
- module = importlib.import_module('mesonbuild.modules.' + modname)
- except ImportError:
- raise InvalidArguments(f'Module "{modname}" does not exist')
- ext_module = module.initialize(self)
- assert isinstance(ext_module, ModuleObject)
- self.modules[modname] = ext_module
-
- return ext_module
+ return self._import_module(modname, required)
@stringArgs
@noKwargs
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 3c3ecf6..b92b66f 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -132,3 +132,8 @@ class FuncInstallMan(TypedDict):
install_dir: T.Optional[str]
install_mode: FileMode
locale: T.Optional[str]
+
+
+class FuncImportModule(ExtractRequired):
+
+ disabler: bool
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index ab8534e..19de1bd 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -106,6 +106,7 @@ class ModuleObject(HoldableObject):
class MutableModuleObject(ModuleObject):
pass
+
# FIXME: Port all modules to stop using self.interpreter and use API on
# ModuleState instead. Modules should stop using this class and instead use
# ModuleObject base class.
@@ -119,7 +120,11 @@ class ExtensionModule(ModuleObject):
@noPosargs
@noKwargs
- def found_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
+ def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
+ return self.found()
+
+ @staticmethod
+ def found() -> bool:
return True
@@ -130,7 +135,7 @@ class NewExtensionModule(ModuleObject):
provides the found method.
"""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
self.methods.update({
'found': self.found_method,
@@ -138,26 +143,23 @@ class NewExtensionModule(ModuleObject):
@noPosargs
@noKwargs
- def found_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
+ def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
+ return self.found()
+
+ @staticmethod
+ def found() -> bool:
return True
-class NotFoundExtensionModule(ModuleObject):
+class NotFoundExtensionModule(NewExtensionModule):
"""Class for modern modules
provides the found method.
"""
- def __init__(self):
- super().__init__()
- self.methods.update({
- 'found': self.found_method,
- })
-
- @noPosargs
- @noKwargs
- def found_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
+ @staticmethod
+ def found() -> bool:
return False
diff --git a/test cases/common/67 modules/meson.build b/test cases/common/67 modules/meson.build
index e9750cd..ad33ed6 100644
--- a/test cases/common/67 modules/meson.build
+++ b/test cases/common/67 modules/meson.build
@@ -2,3 +2,13 @@ project('module test', 'c')
modtest = import('modtest')
modtest.print_hello()
+assert(modtest.found())
+
+modtest = import('modtest', required : get_option('disabled'))
+assert(not modtest.found())
+
+notfound = import('not-found', required : false)
+assert(not notfound.found())
+
+disabled = import('not-found', required : false, disabler : true)
+assert(is_disabler(disabled))
diff --git a/test cases/common/67 modules/meson_options.txt b/test cases/common/67 modules/meson_options.txt
new file mode 100644
index 0000000..0671144
--- /dev/null
+++ b/test cases/common/67 modules/meson_options.txt
@@ -0,0 +1,6 @@
+option(
+ 'disabled',
+ type : 'feature',
+ value : 'disabled',
+ description : 'test disabled'
+)
diff --git a/test cases/failing/57 kwarg in module/meson.build b/test cases/failing/57 kwarg in module/meson.build
deleted file mode 100644
index b105db1..0000000
--- a/test cases/failing/57 kwarg in module/meson.build
+++ /dev/null
@@ -1,5 +0,0 @@
-project('module test', 'c')
-
-modtest = import('modtest', i_cause: 'a_build_failure')
-modtest.print_hello()
-
diff --git a/test cases/failing/57 kwarg in module/test.json b/test cases/failing/57 kwarg in module/test.json
deleted file mode 100644
index cafb3ab..0000000
--- a/test cases/failing/57 kwarg in module/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/57 kwarg in module/meson.build:3:0: ERROR: Function does not take keyword arguments."
- }
- ]
-}