aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/modules
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2020-10-21 16:07:31 -0700
committerDylan Baker <dylan@pnwbakers.com>2021-01-05 15:10:50 -0800
commit3d80a88bd3c3dc8f9e20bbda485b0b436fd79fb3 (patch)
treed8c4fe8fd2c76920a42e4090348681169974f2af /mesonbuild/modules
parentb2c2549b93a8001d8a6d9d6da1ce756645e59160 (diff)
downloadmeson-3d80a88bd3c3dc8f9e20bbda485b0b436fd79fb3.zip
meson-3d80a88bd3c3dc8f9e20bbda485b0b436fd79fb3.tar.gz
meson-3d80a88bd3c3dc8f9e20bbda485b0b436fd79fb3.tar.bz2
modules: Add an unstable-rust module
Like other language specific modules this module is module for holding rust specific helpers. This commit adds a test() function, which simplifies using rust's internal unittest mechanism. Rust tests are generally placed in the same code files as they are testing, in contrast to languages like C/C++ and python which generally place the tests in separate translation units. For meson this is somewhat problematic from a repetition point of view, as the only changes are generally adding --test, and possibly some dependencies. The rustmod.test() method provides a mechanism to remove the repatition: it takes a rust target, copies it, and then addes the `--test` option, then creates a Test() target with the `rust` protocol. You can pass additional dependencies via the `dependencies` keyword. This all makes for a nice, DRY, test definition.
Diffstat (limited to 'mesonbuild/modules')
-rw-r--r--mesonbuild/modules/unstable_rust.py137
1 files changed, 137 insertions, 0 deletions
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py
new file mode 100644
index 0000000..72b5217
--- /dev/null
+++ b/mesonbuild/modules/unstable_rust.py
@@ -0,0 +1,137 @@
+# Copyright © 2020 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import typing as T
+
+from . import ExtensionModule, ModuleReturnValue
+from .. import mlog
+from ..build import BuildTarget, Executable, InvalidArguments
+from ..dependencies import Dependency, ExternalLibrary
+from ..interpreter import ExecutableHolder, permitted_kwargs
+from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew
+from ..mesonlib import stringlistify, unholder, listify
+
+if T.TYPE_CHECKING:
+ from ..interpreter import ModuleState, Interpreter
+
+
+class RustModule(ExtensionModule):
+
+ """A module that holds helper functions for rust."""
+
+ @FeatureNew('rust module', '0.57.0')
+ def __init__(self, interpreter: 'Interpreter') -> None:
+ super().__init__(interpreter)
+
+ @permittedKwargs(permitted_kwargs['test'] | {'dependencies'} ^ {'protocol'})
+ def test(self, state: 'ModuleState', args: T.List, kwargs: T.Dict[str, T.Any]) -> ModuleReturnValue:
+ """Generate a rust test target from a given rust target.
+
+ Rust puts it's unitests inside it's main source files, unlike most
+ languages that put them in external files. This means that normally
+ you have to define two seperate targets with basically the same
+ arguments to get tests:
+
+ ```meson
+ rust_lib_sources = [...]
+ rust_lib = static_library(
+ 'rust_lib',
+ rust_lib_sources,
+ )
+
+ rust_lib_test = executable(
+ 'rust_lib_test',
+ rust_lib_sources,
+ rust_args : ['--test'],
+ )
+
+ test(
+ 'rust_lib_test',
+ rust_lib_test,
+ protocol : 'rust',
+ )
+ ```
+
+ This is all fine, but not very DRY. This method makes it much easier
+ to define rust tests:
+
+ ```meson
+ rust = import('unstable-rust')
+
+ rust_lib = static_library(
+ 'rust_lib',
+ [sources],
+ )
+
+ rust.test('rust_lib_test', rust_lib)
+ ```
+ """
+ if len(args) != 2:
+ raise InterpreterException('rustmod.test() takes exactly 2 positional arguments')
+ name: str = args[0]
+ if not isinstance(name, str):
+ raise InterpreterException('First positional argument to rustmod.test() must be a string')
+ base_target: BuildTarget = unholder(args[1])
+ if not isinstance(base_target, BuildTarget):
+ raise InterpreterException('Second positional argument to rustmod.test() must be a library or executable')
+ if not base_target.get_using_rustc():
+ raise InterpreterException('Second positional argument to rustmod.test() must be a rust based target')
+ extra_args = stringlistify(kwargs.get('args', []))
+
+ # Delete any arguments we don't want passed
+ if '--test' in extra_args:
+ mlog.warning('Do not add --test to rustmod.test arguments')
+ extra_args.remove('--test')
+ if '--format' in extra_args:
+ mlog.warning('Do not add --format to rustmod.test arguments')
+ i = extra_args.index('--format')
+ # Also delete the argument to --format
+ del extra_args[i + 1]
+ del extra_args[i]
+ for i, a in enumerate(extra_args):
+ if a.startswith('--format='):
+ del extra_args[i]
+ break
+
+ dependencies = unholder(listify(kwargs.get('dependencies', [])))
+ for d in dependencies:
+ if not isinstance(d, (Dependency, ExternalLibrary)):
+ raise InvalidArguments('dependencies must be a dependency or external library')
+
+ kwargs['args'] = extra_args + ['--test', '--format', 'pretty']
+ kwargs['protocol'] = 'rust'
+
+ new_target_kwargs = base_target.kwargs.copy()
+ # Don't mutate the shallow copied list, instead replace it with a new
+ # one
+ new_target_kwargs['rust_args'] = new_target_kwargs.get('rust_args', []) + ['--test']
+ new_target_kwargs['install'] = False
+ new_target_kwargs['dependencies'] = new_target_kwargs.get('dependencies', []) + dependencies
+
+ new_target = Executable(
+ name, base_target.subdir, state.subproject,
+ base_target.for_machine, base_target.sources,
+ base_target.objects, base_target.environment,
+ new_target_kwargs
+ )
+
+ e = ExecutableHolder(new_target, self.interpreter)
+ test = self.interpreter.make_test(
+ self.interpreter.current_node, [name, e], kwargs)
+
+ return ModuleReturnValue([], [e, test])
+
+
+def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule:
+ return RustModule(*args, **kwargs) # type: ignore