cargo: Add support for `system-deps` dependencies

pull/13815/head
Thibault Saunier 2 years ago committed by Xavier Claessens
parent 6736a540c7
commit db82c2d777
  1. 79
      mesonbuild/cargo/interpreter.py
  2. 5
      test cases/rust/26 cargo system deps/main.rs
  3. 11
      test cases/rust/26 cargo system deps/meson.build
  4. 2
      test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap
  5. 16
      test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml
  6. 15
      test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs

@ -144,7 +144,7 @@ class Package:
exclude: T.List[str] = dataclasses.field(default_factory=list) exclude: T.List[str] = dataclasses.field(default_factory=list)
include: T.List[str] = dataclasses.field(default_factory=list) include: T.List[str] = dataclasses.field(default_factory=list)
publish: bool = True 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 default_run: T.Optional[str] = None
autobins: bool = True autobins: bool = True
autoexamples: bool = True autoexamples: bool = True
@ -155,6 +155,45 @@ class Package:
def __post_init__(self) -> None: def __post_init__(self) -> None:
self.api = _version_to_api(self.version) 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 @dataclasses.dataclass
class Dependency: class Dependency:
@ -289,6 +328,7 @@ class Manifest:
dependencies: T.Dict[str, Dependency] dependencies: T.Dict[str, Dependency]
dev_dependencies: T.Dict[str, Dependency] dev_dependencies: T.Dict[str, Dependency]
build_dependencies: T.Dict[str, Dependency] build_dependencies: T.Dict[str, Dependency]
system_dependencies: T.Dict[str, SystemDependency] = dataclasses.field(init=False)
lib: Library lib: Library
bin: T.List[Binary] bin: T.List[Binary]
test: T.List[Test] test: T.List[Test]
@ -300,6 +340,7 @@ class Manifest:
def __post_init__(self) -> None: def __post_init__(self) -> None:
self.features.setdefault('default', []) 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: def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = '') -> Manifest:
@ -563,8 +604,38 @@ class Interpreter:
for depname in pkg.required_deps: for depname in pkg.required_deps:
dep = pkg.manifest.dependencies[depname] dep = pkg.manifest.dependencies[depname]
ast += self._create_dependency(dep, build) 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 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]: def _create_dependency(self, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]:
pkg = self._dep_package(dep) pkg = self._dep_package(dep)
kw = { kw = {
@ -654,10 +725,14 @@ class Interpreter:
dep_pkg = self._dep_package(dep) dep_pkg = self._dep_package(dep)
dep_lib_name = dep_pkg.manifest.lib.name dep_lib_name = dep_pkg.manifest.lib.name
dependency_map[build.string(fixup_meson_varname(dep_lib_name))] = build.string(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] = [ rust_args: T.List[mparser.BaseNode] = [
build.identifier('features_args'), 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())) dependencies.append(build.identifier(_extra_deps_varname()))

@ -0,0 +1,5 @@
extern crate sub;
pub fn main() {
sub::func();
}

@ -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)

@ -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"

@ -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()
}
Loading…
Cancel
Save