diff options
Diffstat (limited to 'mesonbuild/modules/rust.py')
| -rw-r--r-- | mesonbuild/modules/rust.py | 79 |
1 files changed, 75 insertions, 4 deletions
diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 7e599505d..fdb18261e 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -4,6 +4,7 @@ from __future__ import annotations import itertools import os +import re import typing as T from mesonbuild.interpreterbase.decorators import FeatureNew @@ -11,15 +12,16 @@ from mesonbuild.interpreterbase.decorators import FeatureNew from . import ExtensionModule, ModuleReturnValue, ModuleInfo from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, - CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary) + CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( - DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, OUTPUT_KW, + DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noPosargs, permittedKwargs -from ..mesonlib import File -from ..programs import ExternalProgram +from ..interpreter.interpreterobjects import Doctest +from ..mesonlib import File, MesonException, PerMachine +from ..programs import ExternalProgram, NonExistingExternalProgram if T.TYPE_CHECKING: from . import ModuleState @@ -45,6 +47,7 @@ if T.TYPE_CHECKING: rust_args: T.List[str] FuncTest = FuncRustTest[_kwargs.TestArgs] + FuncDoctest = FuncRustTest[str] class FuncBindgen(TypedDict): @@ -70,6 +73,11 @@ RUST_TEST_KWS: T.List[KwargInfo] = [ KwargInfo('is_parallel', bool, default=False), ] +def no_spaces_validator(arg: T.Optional[T.Union[str, T.List]]) -> T.Optional[str]: + if any(bool(re.search(r'\s', x)) for x in arg): + return 'must not contain spaces due to limitations of rustdoc' + return None + class RustModule(ExtensionModule): @@ -77,6 +85,7 @@ class RustModule(ExtensionModule): INFO = ModuleInfo('rust', '0.57.0', stabilized='1.0.0') _bindgen_rust_target: T.Optional[str] + rustdoc: PerMachine[T.Optional[ExternalProgram]] = PerMachine(None, None) def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) @@ -89,6 +98,7 @@ class RustModule(ExtensionModule): self._bindgen_set_std = False self.methods.update({ 'test': self.test, + 'doctest': self.doctest, 'bindgen': self.bindgen, 'proc_macro': self.proc_macro, }) @@ -207,6 +217,67 @@ class RustModule(ExtensionModule): return ModuleReturnValue(None, [new_target, test]) + @FeatureNew('rust.doctest', '1.8.0') + @typed_pos_args('rust.doctest', str, BuildTarget) + @typed_kwargs( + 'rust.doctest', + *TEST_KWS_NO_ARGS, + DEPENDENCIES_KW, + LINK_WITH_KW, + *RUST_TEST_KWS, + KwargInfo( + 'args', + ContainerTypeInfo(list, str), + listify=True, + default=[], + validator=no_spaces_validator, + ), + ) + def doctest(self, state: ModuleState, args: T.Tuple[str, T.Union[SharedLibrary, StaticLibrary]], kwargs: FuncDoctest) -> ModuleReturnValue: + name, base_target = args + + # Link the base target's crate into the tests + kwargs['link_with'].append(base_target) + kwargs['depends'].append(base_target) + workdir = kwargs['workdir'] + kwargs['workdir'] = None + new_target, tkwargs = self.test_common('doctest', state, args, kwargs) + + # added automatically by rustdoc; keep things simple + tkwargs['args'].remove('--test') + + # --test-args= is "parsed" simply via the Rust function split_whitespace(). + # This means no quoting nightmares (pfew) but it also means no spaces. + # Unfortunately it's pretty hard at this point to accept e.g. CustomTarget, + # because their paths may not be known. This is not a big deal because the + # user does not control the test harness, so make things easy and allow + # strings only. + if tkwargs['args']: + tkwargs['args'] = ['--test-args=' + ' '.join(T.cast('T.Sequence[str]', tkwargs['args']))] + if workdir: + tkwargs['args'].append('--test-run-directory=' + workdir) + + if self.rustdoc[base_target.for_machine] is None: + rustc = T.cast('RustCompiler', base_target.compilers['rust']) + rustdoc = rustc.get_rustdoc(state.environment) + if rustdoc: + self.rustdoc[base_target.for_machine] = ExternalProgram(rustdoc.get_exe()) + else: + self.rustdoc[base_target.for_machine] = NonExistingExternalProgram() + + rustdoc_prog = self.rustdoc[base_target.for_machine] + if not rustdoc_prog.found(): + raise MesonException(f'could not find rustdoc for {base_target.for_machine} machine') + + doctests: Doctest = self.interpreter.make_test( + self.interpreter.current_node, (name, rustdoc_prog), tkwargs, Doctest) + + # Note that the new_target is intentionally not returned, as it + # is only reached via the base_target and never built by "ninja" + doctests.target = new_target + base_target.doctests = doctests + return ModuleReturnValue(None, [doctests]) + @noPosargs @typed_kwargs( 'rust.bindgen', |
