diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2021-01-06 11:28:50 -0800 |
---|---|---|
committer | Dylan Baker <dylan@pnwbakers.com> | 2021-02-06 10:27:04 -0800 |
commit | b28235428df69ba0a90a8f7c0f26db8527ec68a7 (patch) | |
tree | 10df8a0b264bf493cbf7de3807e5a56a97d959ab /mesonbuild/modules | |
parent | 9d441d26d20a8bd79edd2180160238771fe8c28d (diff) | |
download | meson-b28235428df69ba0a90a8f7c0f26db8527ec68a7.zip meson-b28235428df69ba0a90a8f7c0f26db8527ec68a7.tar.gz meson-b28235428df69ba0a90a8f7c0f26db8527ec68a7.tar.bz2 |
rust: Add a module wrapper for bindgen
This has a couple of advantages over rolling it by hand:
1. it correctly handles include_directories objects, which is always
handy
2. it correctly generates a depfile for you, which makes it more
reliable
3. it requires less typing
Diffstat (limited to 'mesonbuild/modules')
-rw-r--r-- | mesonbuild/modules/unstable_rust.py | 80 |
1 files changed, 77 insertions, 3 deletions
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py index e74c181..c4d7d41 100644 --- a/mesonbuild/modules/unstable_rust.py +++ b/mesonbuild/modules/unstable_rust.py @@ -12,18 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import typing as T from . import ExtensionModule, ModuleReturnValue from .. import mlog -from ..build import BuildTarget, Executable, InvalidArguments +from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget from ..dependencies import Dependency, ExternalLibrary -from ..interpreter import ExecutableHolder, BuildTargetHolder, permitted_kwargs +from ..interpreter import ExecutableHolder, BuildTargetHolder, CustomTargetHolder, permitted_kwargs, noPosargs from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew, typed_pos_args -from ..mesonlib import stringlistify, unholder, listify +from ..mesonlib import stringlistify, unholder, listify, typeslistify, File if T.TYPE_CHECKING: from ..interpreter import ModuleState, Interpreter + from ..dependencies import ExternalProgram class RustModule(ExtensionModule): @@ -33,6 +35,7 @@ class RustModule(ExtensionModule): @FeatureNew('rust module', '0.57.0') def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) + self._bindgen_bin: T.Optional['ExternalProgram'] = None @permittedKwargs(permitted_kwargs['test'] | {'dependencies'} ^ {'protocol'}) @typed_pos_args('rust.test', str, BuildTargetHolder) @@ -127,6 +130,77 @@ class RustModule(ExtensionModule): return ModuleReturnValue([], [e, test]) + @noPosargs + @permittedKwargs({'input', 'output', 'include_directories', 'c_args', 'args'}) + def bindgen(self, state: 'ModuleState', args: T.List, kwargs: T.Dict[str, T.Any]) -> ModuleReturnValue: + """Wrapper around bindgen to simplify it's use. + + The main thing this simplifies is the use of `include_directory` + objects, instead of having to pass a plethora of `-I` arguments. + """ + header: T.Union[File, CustomTarget, GeneratedList, CustomTargetIndex] + _deps: T.Sequence[T.Union[File, CustomTarget, GeneratedList, CustomTargetIndex]] + try: + header, *_deps = unholder(self.interpreter.source_strings_to_files(listify(kwargs['input']))) + except KeyError: + raise InvalidArguments('rustmod.bindgen() `input` argument must have at least one element.') + + try: + output: str = kwargs['output'] + except KeyError: + raise InvalidArguments('rustmod.bindgen() `output` must be provided') + if not isinstance(output, str): + raise InvalidArguments('rustmod.bindgen() `output` argument must be a string.') + + include_dirs: T.List[IncludeDirs] = typeslistify(unholder(listify(kwargs.get('include_directories', []))), IncludeDirs) + c_args: T.List[str] = stringlistify(listify(kwargs.get('c_args', []))) + bind_args: T.List[str] = stringlistify(listify(kwargs.get('args', []))) + + # Split File and Target dependencies to add pass to CustomTarget + depends: T.List[BuildTarget] = [] + depend_files: T.List[File] = [] + for d in _deps: + if isinstance(d, File): + depend_files.append(d) + else: + depends.append(d) + + inc_strs: T.List[str] = [] + for i in include_dirs: + # bindgen always uses clang, so it's safe to hardcode -I here + inc_strs.extend([f'-I{x}' for x in i.to_string_list(state.environment.get_source_dir())]) + + if self._bindgen_bin is None: + # there's some bugs in the interpreter typeing. + self._bindgen_bin = T.cast('ExternalProgram', self.interpreter.find_program_impl(['bindgen']).held_object) + + name: str + if isinstance(header, File): + name = header.fname + else: + name = header.get_outputs()[0] + + target = CustomTarget( + f'rustmod-bindgen-{name}'.replace('/', '_'), + state.subdir, + state.subproject, + { + 'input': header, + 'output': output, + 'command': self._bindgen_bin.get_command() + [ + '@INPUT@', '--output', + os.path.join(state.environment.build_dir, '@OUTPUT@')] + + bind_args + ['--'] + c_args + inc_strs + + ['-MD', '-MQ', '@INPUT@', '-MF', '@DEPFILE@'], + 'depfile': '@PLAINNAME@.d', + 'depends': depends, + 'depend_files': depend_files, + }, + backend=state.backend, + ) + + return ModuleReturnValue([target], [CustomTargetHolder(target, self.interpreter)]) + def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule: return RustModule(*args, **kwargs) # type: ignore |