From db82c2d777b05a1df076e39a470ab0cbce31a184 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Sat, 22 Apr 2023 13:51:47 -0400 Subject: [PATCH] cargo: Add support for `system-deps` dependencies --- mesonbuild/cargo/interpreter.py | 79 ++++++++++++++++++- test cases/rust/26 cargo system deps/main.rs | 5 ++ .../rust/26 cargo system deps/meson.build | 11 +++ .../subprojects/sub-1-rs.wrap | 2 + .../subprojects/sub-1-rs/Cargo.toml | 16 ++++ .../subprojects/sub-1-rs/lib.rs | 15 ++++ 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 test cases/rust/26 cargo system deps/main.rs create mode 100644 test cases/rust/26 cargo system deps/meson.build create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index f8f73a3b2..00e713622 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -144,7 +144,7 @@ class Package: exclude: T.List[str] = dataclasses.field(default_factory=list) include: T.List[str] = dataclasses.field(default_factory=list) publish: bool = True - metadata: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict) + metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict) default_run: T.Optional[str] = None autobins: bool = True autoexamples: bool = True @@ -155,6 +155,45 @@ class Package: def __post_init__(self) -> None: self.api = _version_to_api(self.version) +@dataclasses.dataclass +class SystemDependency: + + """ Representation of a Cargo system-deps entry + https://docs.rs/system-deps/latest/system_deps + """ + + name: str + version: T.List[str] + optional: bool = False + feature: T.Optional[str] = None + feature_overrides: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict) + + @classmethod + def from_raw(cls, name: str, raw: T.Any) -> SystemDependency: + if isinstance(raw, str): + return cls(name, SystemDependency.convert_version(raw)) + name = raw.get('name', name) + version = SystemDependency.convert_version(raw.get('version')) + optional = raw.get('optional', False) + feature = raw.get('feature') + # Everything else are overrides when certain features are enabled. + feature_overrides = {k: v for k, v in raw.items() if k not in {'name', 'version', 'optional', 'feature'}} + return cls(name, version, optional, feature, feature_overrides) + + @staticmethod + def convert_version(version: T.Optional[str]) -> T.List[str]: + vers = version.split(',') if version is not None else [] + result: T.List[str] = [] + for v in vers: + v = v.strip() + if v[0] not in '><=': + v = f'>={v}' + result.append(v) + return result + + def enabled(self, features: T.Set[str]) -> bool: + return self.feature is None or self.feature in features + @dataclasses.dataclass class Dependency: @@ -289,6 +328,7 @@ class Manifest: dependencies: T.Dict[str, Dependency] dev_dependencies: T.Dict[str, Dependency] build_dependencies: T.Dict[str, Dependency] + system_dependencies: T.Dict[str, SystemDependency] = dataclasses.field(init=False) lib: Library bin: T.List[Binary] test: T.List[Test] @@ -300,6 +340,7 @@ class Manifest: def __post_init__(self) -> None: self.features.setdefault('default', []) + self.system_dependencies = {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()} def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = '') -> Manifest: @@ -563,8 +604,38 @@ class Interpreter: for depname in pkg.required_deps: dep = pkg.manifest.dependencies[depname] ast += self._create_dependency(dep, build) + ast.append(build.assign(build.array([]), 'system_deps_args')) + for name, sys_dep in pkg.manifest.system_dependencies.items(): + if sys_dep.enabled(pkg.features): + ast += self._create_system_dependency(name, sys_dep, build) return ast + def _create_system_dependency(self, name: str, dep: SystemDependency, build: builder.Builder) -> T.List[mparser.BaseNode]: + kw = { + 'version': build.array([build.string(s) for s in dep.version]), + 'required': build.bool(not dep.optional), + } + varname = f'{fixup_meson_varname(name)}_system_dep' + cfg = f'system_deps_have_{fixup_meson_varname(name)}' + return [ + build.assign( + build.function( + 'dependency', + [build.string(dep.name)], + kw, + ), + varname, + ), + build.if_( + build.method('found', build.identifier(varname)), build.block([ + build.plusassign( + build.array([build.string('--cfg'), build.string(cfg)]), + 'system_deps_args' + ), + ]) + ), + ] + def _create_dependency(self, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]: pkg = self._dep_package(dep) kw = { @@ -654,10 +725,14 @@ class Interpreter: dep_pkg = self._dep_package(dep) dep_lib_name = dep_pkg.manifest.lib.name dependency_map[build.string(fixup_meson_varname(dep_lib_name))] = build.string(name) + for name, sys_dep in pkg.manifest.system_dependencies.items(): + if sys_dep.enabled(pkg.features): + dependencies.append(build.identifier(f'{fixup_meson_varname(name)}_system_dep')) rust_args: T.List[mparser.BaseNode] = [ build.identifier('features_args'), - build.identifier(_extra_args_varname()) + build.identifier(_extra_args_varname()), + build.identifier('system_deps_args'), ] dependencies.append(build.identifier(_extra_deps_varname())) diff --git a/test cases/rust/26 cargo system deps/main.rs b/test cases/rust/26 cargo system deps/main.rs new file mode 100644 index 000000000..25e3b2fb8 --- /dev/null +++ b/test cases/rust/26 cargo system deps/main.rs @@ -0,0 +1,5 @@ +extern crate sub; + +pub fn main() { + sub::func(); +} diff --git a/test cases/rust/26 cargo system deps/meson.build b/test cases/rust/26 cargo system deps/meson.build new file mode 100644 index 000000000..9545bb452 --- /dev/null +++ b/test cases/rust/26 cargo system deps/meson.build @@ -0,0 +1,11 @@ +project('cargo system-deps', 'rust') + +glib = dependency('glib-2.0', required: false) +if not glib.found() + error('MESON_SKIP_TEST: Need glib system dependency') +endif + +sub_dep = dependency('sub-1-rs') +exe = executable('main', 'main.rs', dependencies : sub_dep) +test('main', exe) + diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap new file mode 100644 index 000000000..99686e90e --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap @@ -0,0 +1,2 @@ +[wrap-file] +method = cargo diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml new file mode 100644 index 000000000..88d5445f6 --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = 'sub' +version = '1' + +[build-dependencies] +system-deps = "6" + +[lib] +name = "sub" +path = "lib.rs" + +[package.metadata.system-deps] +glib = { name = "glib-2.0", version=" 2.0 , 2.1 , <3 ", feature="default" } +gobject = { name = "gobject-2.0", version=">=99", optional=true } +notfound = { feature="notfound" } +libffi = "1.0" diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs new file mode 100644 index 000000000..6e39d2365 --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs @@ -0,0 +1,15 @@ +extern "C" { + fn g_get_tmp_dir() -> *mut std::ffi::c_void; +} + +#[cfg(system_deps_have_glib)] +#[cfg(not(system_deps_have_gobject))] +pub fn func() { + unsafe { + g_get_tmp_dir(); + } +} + +pub fn func1() { + func() +}