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.
pull/7860/head
Dylan Baker 4 years ago
parent b2c2549b93
commit 3d80a88bd3
  1. 35
      docs/markdown/Rust-module.md
  2. 1
      docs/markdown/_Sidebar.md
  3. 4
      docs/markdown/snippets/unstable-rust-module.md
  4. 11
      docs/sitemap.txt
  5. 45
      docs/theme/extra/templates/navbar_links.html
  6. 137
      mesonbuild/modules/unstable_rust.py
  7. 1
      run_mypy.py
  8. 11
      test cases/rust/9 unit tests/meson.build
  9. 11
      test cases/rust/9 unit tests/test2.rs

@ -0,0 +1,35 @@
---
short-description: Rust language integration module
authors:
- name: Dylan Baker
email: dylan@pnwbakers.com
years: [2020]
...
# Unstable Rust module
*(new in 0.57.0)*
**Note** Unstable modules make no backwards compatible API guarantees.
The rust module provides helper to integrate rust code into meson. The goal
is to make using rust in meson more pleasant, while still remaining mesonic,
this means that it attempts to make rust work more like meson, rather than
meson work more like rust.
## Functions
### test(name: string, target: library | executable, dependencies: []Dependency)
This function creates a new rust unittest target from an existing rust based
target, which may be a library or executable. It does this by copying the
sources and arguments passed to the original target and adding the `--test`
argument to the compilation, then creates a new test target which calls that
executable, using the rust test protocol.
This accepts all of the keyword arguments as the
[`test`](Reference-manual.md#test) function except `protocol`, it will set
that automatically.
Additional, test only dependencies may be passed via the dependencies
argument.

@ -12,3 +12,4 @@
* [gnome](Gnome-module.md)
* [i18n](i18n-module.md)
* [pkgconfig](Pkgconfig-module.md)
* [rust](Rust-module.md)

@ -0,0 +1,4 @@
## Untable Rust module
A new unstable module has been added to make using rust with meson easier.
Currently it adds a single function to ease defining rust tests.

@ -34,24 +34,25 @@ index.md
Disabler.md
Modules.md
CMake-module.md
Cuda-module.md
Dlang-module.md
External-Project-module.md
Fs-module.md
Gnome-module.md
Hotdoc-module.md
i18n-module.md
Icestorm-module.md
Keyval-module.md
Pkgconfig-module.md
Python-module.md
Python-3-module.md
Python-module.md
Qt4-module.md
Qt5-module.md
RPM-module.md
Rust-module.md
Simd-module.md
SourceSet-module.md
Windows-module.md
Cuda-module.md
Keyval-module.md
External-Project-module.md
i18n-module.md
Java.md
Vala.md
D.md

@ -5,28 +5,29 @@
Modules <span class="caret"></span>
</a>
<ul class="dropdown-menu" id="modules-menu">
@for tup in ( \
("CMake-module.html","CMake"), \
("Cuda-module.html","CUDA"), \
("Dlang-module.html","Dlang"), \
("Fs-module.html","Filesystem"), \
("Gnome-module.html","GNOME"), \
("Hotdoc-module.html","Hotdoc"), \
("i18n-module.html","i18n"), \
("Icestorm-module.html","Icestorm"), \
("Keyval-module.html","Keyval"), \
("Pkgconfig-module.html","Pkgconfig"), \
("Python-module.html","Python"), \
("Python-3-module.html","Python 3"), \
("Qt4-module.html","Qt4"), \
("Qt5-module.html","Qt5"), \
("RPM-module.html","RPM"), \
("SourceSet-module.html","SourceSet"), \
("Windows-module.html","Windows")):
<li>
<a href="@tup[0]">@tup[1]</a>
</li>
@end
@for tup in [ \
("CMake-module.html","CMake"), \
("Cuda-module.html","CUDA"), \
("Dlang-module.html","Dlang"), \
("Fs-module.html","Filesystem"), \
("Gnome-module.html","GNOME"), \
("Hotdoc-module.html","Hotdoc"), \
("Icestorm-module.html","Icestorm"), \
("Keyval-module.html","Keyval"), \
("Pkgconfig-module.html","Pkgconfig"), \
("Python-3-module.html","Python 3"), \
("Python-module.html","Python"), \
("Qt4-module.html","Qt4"), \
("Qt5-module.html","Qt5"), \
("RPM-module.html","RPM"), \
("Rust-module.html","Rust"), \
("SourceSet-module.html","SourceSet"), \
("Windows-module.html","Windows"), \
("i18n-module.html","i18n")]:
<li>
<a href="@tup[0]">@tup[1]</a>
</li>
@end
</ul>
</li>
\

@ -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

@ -29,6 +29,7 @@ modules = [
'mesonbuild/mintro.py',
'mesonbuild/mlog.py',
'mesonbuild/modules/fs.py',
'mesonbuild/modules/unstable_rust.py',
'mesonbuild/mparser.py',
'mesonbuild/msetup.py',
'mesonbuild/mtest.py',

@ -30,3 +30,14 @@ test(
protocol : 'rust',
suite : ['foo'],
)
exe = executable('rust_exe', ['test2.rs', 'test.rs'])
rust = import('unstable-rust')
rust.test('rust_test_from_exe', exe, should_fail : true)
lib = static_library('rust_static', ['test.rs'])
rust.test('rust_test_from_static', lib, args: ['--skip', 'test_add_intentional_fail'])
lib = shared_library('rust_shared', ['test.rs'])
rust.test('rust_test_from_shared', lib, args: ['--skip', 'test_add_intentional_fail'])

@ -0,0 +1,11 @@
mod test;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let first = args[1].parse::<i32>().expect("Invliad value for first argument.");
let second = args[2].parse::<i32>().expect("Invliad value for second argument.");
let new = test::add(first, second);
println!("New value: {}", new);
}
Loading…
Cancel
Save