diff options
| -rw-r--r-- | docs/markdown/Rust-module.md | 30 | ||||
| -rw-r--r-- | mesonbuild/cargo/interpreter.py | 89 | ||||
| -rw-r--r-- | mesonbuild/modules/rust.py | 125 |
3 files changed, 191 insertions, 53 deletions
diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 1acb9324b..7ad9f1cfd 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -381,6 +381,36 @@ Keyword arguments: - `dev_dependencies`: (`bool`, default: false) Whether to include development dependencies (not yet implemented) - `system_dependencies`: (`bool`, default: true) Whether to include system dependencies +#### package.library() + +```meson +lib = pkg.library(...) +``` + +Builds library targets for a workspace package. The method requires that +the package's `Cargo.toml` file contains the `[lib]` section or that it +is discovered from the contents of the file system. Static vs. shared library is +decided based on the crate types in `Cargo.toml` + +Positional arguments: +- `target_name`: (`str`, optional) Name of the binary target to build. +- `sources`: (`StructuredSources`, optional) Source files for the executable. If omitted, + uses the path specified in the `[lib]` section of `Cargo.toml`. + +Accepts all keyword arguments from [[shared_library]] and [[static_library]]. +`rust_abi` must match the crate types and is mandatory if more than one +ABI is exposed by the crate. + +#### package.proc_macro() + +```meson +lib = pkg.proc_macro(...) +``` + +Builds a proc-macro crate for a workspace package. + +Accepts all keyword arguments from [[shared_library]]. + ### Subprojects only #### subproject.dependency() diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index fcdc661a3..fcae28d27 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -47,15 +47,6 @@ def _dependency_name(package_name: str, api: str, suffix: str = '-rs') -> str: return f'{basename}-{api}{suffix}' -def _library_name(name: str, api: str, lib_type: Literal['rust', 'c', 'proc-macro'] = 'rust') -> str: - # Add the API version to the library name to avoid conflicts when multiple - # versions of the same crate are used. The Ninja backend removed everything - # after the + to form the crate name. - if lib_type == 'c': - return name - return f'{name}+{api.replace(".", "_")}' - - def _extra_args_varname() -> str: return 'extra_args' @@ -87,7 +78,7 @@ class PackageConfiguration: dep = manifest.dependencies[name] dep_key = PackageKey(dep.package, dep.api) dep_pkg = self.dep_packages[dep_key] - dep_lib_name = _library_name(dep_pkg.manifest.lib.name, dep_pkg.manifest.package.api) + dep_lib_name = dep_pkg.library_name() dep_crate_name = name if name != dep.package else dep_pkg.manifest.lib.name dependency_map[dep_lib_name] = dep_crate_name return dependency_map @@ -109,6 +100,15 @@ class PackageState: return None return os.path.normpath(os.path.join(self.ws_subdir, self.ws_member)) + def library_name(self, lib_type: RUST_ABI = 'rust') -> str: + # Add the API version to the library name to avoid conflicts when multiple + # versions of the same crate are used. The Ninja backend removed everything + # after the + to form the crate name. + name = fixup_meson_varname(self.manifest.package.name) + if lib_type == 'c': + return name + return f'{name}+{self.manifest.package.api.replace(".", "_")}' + def get_env_dict(self, environment: Environment, subdir: str) -> T.Dict[str, str]: """Get environment variables for this package.""" # Common variables for build.rs and crates @@ -205,17 +205,33 @@ class PackageState: def get_subproject_name(self) -> str: return _dependency_name(self.manifest.package.name, self.manifest.package.api) - def get_dependency_name(self, rust_abi: T.Optional[RUST_ABI]) -> str: - """Get the dependency name for a package with the given ABI.""" + def abi_resolve_default(self, rust_abi: T.Optional[RUST_ABI]) -> RUST_ABI: supported_abis = self.supported_abis() if rust_abi is None: if len(supported_abis) > 1: raise MesonException(f'Package {self.manifest.package.name} support more than one ABI') - rust_abi = next(iter(supported_abis)) + return next(iter(supported_abis)) else: if rust_abi not in supported_abis: raise MesonException(f'Package {self.manifest.package.name} does not support ABI {rust_abi}') + return rust_abi + + def abi_has_shared(self, rust_abi: RUST_ABI) -> bool: + if rust_abi == 'proc-macro': + return True + return ('cdylib' if rust_abi == 'c' else 'dylib') in self.manifest.lib.crate_type + + def abi_has_static(self, rust_abi: RUST_ABI) -> bool: + if rust_abi == 'proc-macro': + return False + crate_type = self.manifest.lib.crate_type + if rust_abi == 'c': + return 'staticlib' in crate_type + return 'lib' in crate_type or 'rlib' in crate_type + def get_dependency_name(self, rust_abi: T.Optional[RUST_ABI]) -> str: + """Get the dependency name for a package with the given ABI.""" + rust_abi = self.abi_resolve_default(rust_abi) package_name = self.manifest.package.name api = self.manifest.package.api @@ -354,17 +370,8 @@ class Interpreter: crate_type = pkg.manifest.lib.crate_type if 'dylib' in crate_type and 'cdylib' in crate_type: raise MesonException('Cannot build both dylib and cdylib due to file name conflict') - abis = pkg.supported_abis() - if 'proc-macro' in abis: - ast.extend(self._create_lib(pkg, build, subdir, 'proc-macro', shared=True)) - if 'rust' in abis: - ast.extend(self._create_lib(pkg, build, subdir, 'rust', - static=('lib' in crate_type or 'rlib' in crate_type), - shared='dylib' in crate_type)) - if 'c' in abis: - ast.extend(self._create_lib(pkg, build, subdir, 'c', - static='staticlib' in crate_type, - shared='cdylib' in crate_type)) + for abi in pkg.supported_abis(): + ast.extend(self._create_lib(pkg, build, subdir, abi)) return ast @@ -796,46 +803,24 @@ class Interpreter: ] def _create_lib(self, pkg: PackageState, build: builder.Builder, subdir: str, - lib_type: RUST_ABI, - static: bool = False, shared: bool = False) -> T.List[mparser.BaseNode]: - pkg_dependencies = build.method('dependencies', build.identifier('pkg_obj')) - extra_deps_ref = build.identifier(_extra_deps_varname()) - dependencies = build.plus(pkg_dependencies, extra_deps_ref) - - package_rust_args = build.method('rust_args', build.identifier('pkg_obj')) - extra_args_ref = build.identifier(_extra_args_varname()) - rust_args = build.plus(package_rust_args, extra_args_ref) - - override_options: T.Dict[mparser.BaseNode, mparser.BaseNode] = { - build.string('rust_std'): build.string(pkg.manifest.package.edition), - } - + lib_type: RUST_ABI) -> T.List[mparser.BaseNode]: posargs: T.List[mparser.BaseNode] = [ - build.string(_library_name(pkg.manifest.lib.name, pkg.manifest.package.api, lib_type)), - build.string(pkg.manifest.lib.path), + build.string(pkg.library_name(lib_type)), ] kwargs: T.Dict[str, mparser.BaseNode] = { - 'dependencies': dependencies, - 'rust_dependency_map': build.method('rust_dependency_map', build.identifier('pkg_obj')), - 'rust_args': rust_args, - 'override_options': build.dict(override_options), + 'dependencies': build.identifier(_extra_deps_varname()), + 'rust_args': build.identifier(_extra_args_varname()), } depname_suffix = '' if lib_type == 'c' else '-rs' depname = _dependency_name(pkg.manifest.package.name, pkg.manifest.package.api, depname_suffix) - lib: mparser.BaseNode if lib_type == 'proc-macro': - lib = build.method('proc_macro', build.identifier('rust'), posargs, kwargs) + lib = build.method('proc_macro', build.identifier('pkg_obj'), posargs, kwargs) else: - if static and shared: - target_type = 'both_libraries' - else: - target_type = 'shared_library' if shared else 'static_library' - kwargs['rust_abi'] = build.string(lib_type) - lib = build.function(target_type, posargs, kwargs) + lib = build.method('library', build.identifier('pkg_obj'), posargs, kwargs) # lib = xxx_library() # dep = declare_dependency() diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 974d50ae6..137d64099 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -19,7 +19,8 @@ from ..compilers.rust import RustSystemDependency from ..dependencies import Dependency from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, - OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator + OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator, + LIBRARY_KWS, _BASE_LANG_KW ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs from ..interpreter.interpreterobjects import Doctest @@ -75,6 +76,11 @@ if T.TYPE_CHECKING: class FuncDependency(TypedDict): rust_abi: T.Optional[RUST_ABI] + class RustPackageLibrary(_kwargs.Library): + dependencies: T.List[T.Union[Dependency, ExternalLibrary]] + link_with: T.List[LibTypes] + link_whole: T.List[LibTypes] + RUST_TEST_KWS: T.List[KwargInfo] = [ KwargInfo( 'rust_args', @@ -234,6 +240,8 @@ class RustPackage(RustCrate): super().__init__(rust_ws, package) self.methods.update({ 'dependencies': self.dependencies_method, + 'library': self.library_method, + 'proc_macro': self.proc_macro_method, }) @noPosargs @@ -271,6 +279,121 @@ class RustPackage(RustCrate): return dependencies + @staticmethod + def validate_pos_args(name: str, args: T.Tuple[ + T.Optional[T.Union[str, StructuredSources]], + T.Optional[StructuredSources]]) -> T.Tuple[T.Optional[str], T.Optional[StructuredSources]]: + if isinstance(args[0], str): + return args[0], args[1] + if args[1] is not None: + raise MesonException(f"{name} only accepts one StructuredSources parameter") + return None, args[0] + + def merge_kw_args(self, state: ModuleState, kwargs: RustPackageLibrary) -> None: + deps = kwargs['dependencies'] + kwargs['dependencies'] = self.dependencies_method(state, [], {}) + kwargs['dependencies'].extend(deps) + + depmap = kwargs['rust_dependency_map'] + kwargs['rust_dependency_map'] = self.rust_dependency_map_method(state, [], {}) + kwargs['rust_dependency_map'].update(depmap) + + rust_args = kwargs['rust_args'] + kwargs['rust_args'] = self.rust_args_method(state, [], {}) + kwargs['rust_args'].extend(rust_args) + + kwargs['override_options'].setdefault('rust_std', self.package.manifest.package.edition) + + def _library_method(self, state: ModuleState, args: T.Tuple[ + T.Optional[T.Union[str, StructuredSources]], + T.Optional[StructuredSources]], kwargs: RustPackageLibrary, + static: bool, shared: bool) -> T.Union[BothLibraries, SharedLibrary, StaticLibrary]: + tgt_args = self.validate_pos_args('package.library', args) + if not self.package.manifest.lib: + raise MesonException("no [lib] section in Cargo package") + + sources: T.Union[StructuredSources, str] + tgt_name, sources = tgt_args + if not tgt_name: + rust_abi: RUST_ABI + if kwargs['rust_crate_type'] is not None: + rust_abi = 'rust' if kwargs['rust_crate_type'] in {'lib', 'rlib', 'dylib', 'proc-macro'} else 'c' + else: + rust_abi = kwargs['rust_abi'] + tgt_name = self.package.library_name(rust_abi) + if not sources: + sources = self.package.manifest.lib.path + + lib_args: T.Tuple[str, SourcesVarargsType] = (tgt_name, [sources]) + self.merge_kw_args(state, kwargs) + + if static and shared: + return state._interpreter.build_both_libraries(state.current_node, lib_args, kwargs) + elif shared: + return state._interpreter.build_target(state.current_node, lib_args, + T.cast('_kwargs.SharedLibrary', kwargs), + SharedLibrary) + else: + return state._interpreter.build_target(state.current_node, lib_args, + T.cast('_kwargs.StaticLibrary', kwargs), + StaticLibrary) + + def _proc_macro_method(self, state: 'ModuleState', args: T.Tuple[ + T.Optional[T.Union[str, StructuredSources]], + T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> SharedLibrary: + kwargs['native'] = MachineChoice.BUILD + kwargs['rust_abi'] = None + kwargs['rust_crate_type'] = 'proc-macro' + kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro'] + result = self._library_method(state, args, kwargs, shared=True, static=False) + return T.cast('SharedLibrary', result) + + @typed_pos_args('package.library', optargs=[(str, StructuredSources), StructuredSources]) + @typed_kwargs( + 'package.library', + *LIBRARY_KWS, + DEPENDENCIES_KW, + LINK_WITH_KW, + LINK_WHOLE_KW, + _BASE_LANG_KW.evolve(name='rust_args'), + ) + def library_method(self, state: ModuleState, args: T.Tuple[ + T.Optional[T.Union[str, StructuredSources]], + T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> T.Union[BothLibraries, SharedLibrary, StaticLibrary]: + if not self.package.manifest.lib: + raise MesonException("no [lib] section in Cargo package") + if kwargs['rust_crate_type'] is not None: + static = kwargs['rust_crate_type'] in {'lib', 'rlib', 'staticlib'} + shared = kwargs['rust_crate_type'] in {'dylib', 'cdylib', 'proc-macro'} + else: + rust_abi = self.package.abi_resolve_default(kwargs['rust_abi']) + static = self.package.abi_has_static(rust_abi) + shared = self.package.abi_has_shared(rust_abi) + if rust_abi == 'proc-macro': + kwargs['rust_crate_type'] = 'proc-macro' + kwargs['rust_abi'] = None + else: + kwargs['rust_abi'] = rust_abi + return self._library_method(state, args, kwargs, static=static, shared=shared) + + @typed_pos_args('package.proc_macro', optargs=[(str, StructuredSources), StructuredSources]) + @typed_kwargs( + 'package.proc_macro', + *SHARED_LIB_KWS, + DEPENDENCIES_KW, + LINK_WITH_KW, + LINK_WHOLE_KW, + _BASE_LANG_KW.evolve(name='rust_args'), + ) + def proc_macro_method(self, state: 'ModuleState', args: T.Tuple[ + T.Optional[T.Union[str, StructuredSources]], + T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> SharedLibrary: + if not self.package.manifest.lib: + raise MesonException("no [lib] section in Cargo package") + if 'proc-macro' not in self.package.manifest.lib.crate_type: + raise MesonException("not a procedural macro crate") + return self._proc_macro_method(state, args, kwargs) + class RustSubproject(RustCrate): """Represents a Cargo subproject.""" |
