Skip to content

Commit 98c28d0

Browse files
committed
cargo: pick exact dependency version from Cargo.toml
A dependency might specify a version that is compatible with the one in Cargo.lock, but not the exact same version. Whenever Cargo.lock contains the package, only use the pinned versions that can be used; update the version in the manifest so that the correct API level is used as well. Originally by Xavier Claessens <xclaessens@netflix.com>, but almost completely rewritten.
1 parent d607d19 commit 98c28d0

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

mesonbuild/cargo/interpreter.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from . import builder, version, cfg
2121
from .toml import load_toml
2222
from .manifest import Manifest, CargoLock, fixup_meson_varname
23-
from ..mesonlib import MesonException, MachineChoice
23+
from ..mesonlib import MesonException, MachineChoice, version_compare_many
2424
from .. import coredata, mlog
2525
from ..wrap.wrap import PackageDefinition
2626

@@ -151,6 +151,19 @@ def _dep_package(self, dep: Dependency) -> PackageState:
151151
_, _, directory = _parse_git_url(dep.git, dep.branch)
152152
dep_pkg, _ = self._fetch_package_from_subproject(dep.package, directory)
153153
else:
154+
# From all available versions from Cargo.lock, pick the most recent
155+
# satisfying the constraints
156+
if self.cargolock:
157+
cargo_lock_pkgs = self.cargolock.named(dep.package)
158+
else:
159+
cargo_lock_pkgs = []
160+
for cargo_pkg in cargo_lock_pkgs:
161+
if version_compare_many(cargo_pkg.version, dep.meson_version):
162+
dep.update_version(f'={cargo_pkg.version}')
163+
break
164+
else:
165+
if not dep.meson_version:
166+
raise MesonException(f'Cannot determine version of cargo package {dep.package}')
154167
dep_pkg, _ = self._fetch_package(dep.package, dep.api)
155168
return dep_pkg
156169

mesonbuild/cargo/manifest.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
from __future__ import annotations
77

8+
from collections import defaultdict
89
import dataclasses
910
import os
1011
import typing as T
1112

1213
from . import version
13-
from ..mesonlib import MesonException, lazy_property
14+
from ..mesonlib import MesonException, lazy_property, Version
1415
from .. import mlog
1516

1617
if T.TYPE_CHECKING:
@@ -254,7 +255,7 @@ def api(self) -> str:
254255
elif v.startswith('='):
255256
api.add(version.api(v[1:].strip()))
256257
if not api:
257-
return '0'
258+
return ''
258259
elif len(api) == 1:
259260
return api.pop()
260261
else:
@@ -280,6 +281,17 @@ def from_raw(cls, name: str, raw_depv: T.Union[raw.FromWorkspace, raw.Dependency
280281
raw_dep = _depv_to_dep(raw_depv)
281282
return cls.from_raw_dict(name, raw_dep, member_path, raw_ws_dep)
282283

284+
def update_version(self, v: str) -> None:
285+
self.version = v
286+
try:
287+
delattr(self, 'api')
288+
except AttributeError:
289+
pass
290+
try:
291+
delattr(self, 'meson_version')
292+
except AttributeError:
293+
pass
294+
283295

284296
@dataclasses.dataclass
285297
class BuildTarget(T.Generic[_R]):
@@ -489,6 +501,14 @@ class CargoLockPackage:
489501
checksum: T.Optional[str] = None
490502
dependencies: T.List[str] = dataclasses.field(default_factory=list)
491503

504+
@lazy_property
505+
def api(self) -> str:
506+
return version.api(self.version)
507+
508+
@lazy_property
509+
def subproject(self) -> str:
510+
return f'{self.name}-{self.api}-rs'
511+
492512
@classmethod
493513
def from_raw(cls, raw: raw.CargoLockPackage) -> CargoLockPackage:
494514
return _raw_to_dataclass(raw, cls, 'Cargo.lock package')
@@ -508,3 +528,15 @@ class CargoLock:
508528
def from_raw(cls, raw: raw.CargoLock) -> CargoLock:
509529
return _raw_to_dataclass(raw, cls, 'Cargo.lock',
510530
package=lambda x: [CargoLockPackage.from_raw(p) for p in x])
531+
532+
def named(self, name: str) -> T.Sequence[CargoLockPackage]:
533+
return self._versions[name]
534+
535+
@lazy_property
536+
def _versions(self) -> T.Dict[str, T.List[CargoLockPackage]]:
537+
versions = defaultdict(list)
538+
for pkg in self.package:
539+
versions[pkg.name].append(pkg)
540+
for pkg_versions in versions.values():
541+
pkg_versions.sort(reverse=True, key=lambda pkg: Version(pkg.version))
542+
return versions

0 commit comments

Comments
 (0)