diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2021-02-07 16:57:39 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-07 16:57:39 +0000 |
commit | a855bcab1ccaff68155374c53896c1a780337f40 (patch) | |
tree | 9f5da58d50d50e74bd0c3741420f72918f161863 /mesonbuild | |
parent | 3f8585676ba6d2c1bcd5d80a44275fdfa695f8c2 (diff) | |
parent | 9cebd29da973553c6e657f7b318ab1d6afbc76e6 (diff) | |
download | meson-a855bcab1ccaff68155374c53896c1a780337f40.zip meson-a855bcab1ccaff68155374c53896c1a780337f40.tar.gz meson-a855bcab1ccaff68155374c53896c1a780337f40.tar.bz2 |
Merge pull request #8162 from dcbaker/wip/2021-01/rust-module-bindgen
Add a wrapper to the rust module for bindgen
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 19 | ||||
-rw-r--r-- | mesonbuild/build.py | 12 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 6 | ||||
-rw-r--r-- | mesonbuild/mesonlib/universal.py | 4 | ||||
-rw-r--r-- | mesonbuild/modules/unstable_rust.py | 80 |
5 files changed, 103 insertions, 18 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3eca3c0..b93ac39 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1597,14 +1597,19 @@ int dummy; args += target.get_extra_args('rust') args += rustc.get_output_args(os.path.join(target.subdir, target.get_filename())) args += self.environment.coredata.get_external_args(target.for_machine, rustc.language) - linkdirs = OrderedDict() + linkdirs = mesonlib.OrderedSet() for d in target.link_targets: - linkdirs[d.subdir] = True - # specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust - # dependency, so that collisions with libraries in rustc's - # sysroot don't cause ambiguity - args += ['--extern', '{}={}'.format(d.name, os.path.join(d.subdir, d.filename))] - for d in linkdirs.keys(): + linkdirs.add(d.subdir) + if d.uses_rust(): + # specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust + # dependency, so that collisions with libraries in rustc's + # sysroot don't cause ambiguity + args += ['--extern', '{}={}'.format(d.name, os.path.join(d.subdir, d.filename))] + else: + # Rust uses -l for non rust dependencies, but we still need to add (shared|static)=foo + _type = 'static' if d.typename == 'static library' else 'shared' + args += ['-l', f'{_type}={d.name}'] + for d in linkdirs: if d == '': d = '.' args += ['-L', d] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 160ee9a..9a4f8b1 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -322,6 +322,13 @@ class IncludeDirs: def get_extra_build_dirs(self): return self.extra_build_dirs + def to_string_list(self, sourcedir: str) -> T.List[str]: + """Convert IncludeDirs object to a list of strings.""" + strlist: T.List[str] = [] + for idir in self.incdirs: + strlist.append(os.path.join(sourcedir, self.curdir, idir)) + return strlist + class ExtractedObjects: ''' Holds a list of sources for which the objects must be extracted @@ -2189,7 +2196,8 @@ class CustomTarget(Target, CommandBase): 'env', ]) - def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False, backend=None): + def __init__(self, name: str, subdir: str, subproject: str, kwargs: T.Dict[str, T.Any], + absolute_paths: bool = False, backend: T.Optional[str] = None): self.typename = 'custom' # TODO expose keyword arg to make MachineChoice.HOST configurable super().__init__(name, subdir, subproject, False, MachineChoice.HOST) @@ -2204,7 +2212,7 @@ class CustomTarget(Target, CommandBase): for k in kwargs: if k not in CustomTarget.known_kwargs: unknowns.append(k) - if len(unknowns) > 0: + if unknowns: mlog.warning('Unknown keyword arguments in target {}: {}'.format(self.name, ', '.join(unknowns))) def get_default_install_dir(self, environment): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 176c1da..f670aec 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -918,7 +918,7 @@ class CustomTargetIndexHolder(TargetHolder): return self.interpreter.backend.get_target_filename_abs(self.held_object) class CustomTargetHolder(TargetHolder): - def __init__(self, target, interp): + def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'): super().__init__(target, interp) self.methods.update({'full_path': self.full_path_method, 'to_list': self.to_list_method, @@ -1132,9 +1132,7 @@ class CompilerHolder(InterpreterObject): for i in incdirs: if not isinstance(i, IncludeDirsHolder): raise InterpreterException('Include directories argument must be an include_directories object.') - for idir in i.held_object.get_incdirs(): - idir = os.path.join(self.environment.get_source_dir(), - i.held_object.get_curdir(), idir) + for idir in i.held_object.to_string_list(self.environment.get_source_dir()): args += self.compiler.get_include_args(idir, False) if not nobuiltins: opts = self.environment.coredata.options diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 4b913a8..7deb24a 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -1262,10 +1262,10 @@ def typeslistify(item: 'T.Union[_T, T.Sequence[_T]]', if isinstance(item, types): item = T.cast(T.List[_T], [item]) if not isinstance(item, list): - raise MesonException('Item must be a list or one of {!r}'.format(types)) + raise MesonException('Item must be a list or one of {!r}, not {!r}'.format(types, type(item))) for i in item: if i is not None and not isinstance(i, types): - raise MesonException('List item must be one of {!r}'.format(types)) + raise MesonException('List item must be one of {!r}, not {!r}'.format(types, type(i))) return item 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 |