diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2025-06-03 12:00:05 +0200 |
|---|---|---|
| committer | Dylan Baker <dylan@pnwbakers.com> | 2025-08-01 07:55:49 -0700 |
| commit | 824f6fccf35dc46f17cff9da0639d23614ccba64 (patch) | |
| tree | 17d6d3a7af6742d166e2e1165a67b7410868c082 /mesonbuild | |
| parent | 939cfa6dd16743a296d772136cc338e2148ee7e5 (diff) | |
| download | meson-824f6fccf35dc46f17cff9da0639d23614ccba64.tar.gz | |
cargo: move TypedDicts for Cargo.toml to "raw" module
Fix a few issues:
* Cargo.lock's version is an int
* Different BuildTargets have different types for the argument of from_raw
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild')
| -rw-r--r-- | mesonbuild/cargo/interpreter.py | 46 | ||||
| -rw-r--r-- | mesonbuild/cargo/manifest.py | 177 | ||||
| -rw-r--r-- | mesonbuild/cargo/raw.py | 179 |
3 files changed, 208 insertions, 194 deletions
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index b6d0817cd..e3918ba7f 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -26,7 +26,7 @@ from ..wrap.wrap import PackageDefinition if T.TYPE_CHECKING: from typing_extensions import Protocol, Self - from . import manifest + from . import manifest, raw from .. import mparser from ..environment import Environment from ..interpreterbase import SubProject @@ -40,6 +40,8 @@ if T.TYPE_CHECKING: manifest.FixedDependency, manifest.FixedLibTarget, manifest.FixedBuildTarget) +_R = T.TypeVar('_R', bound='raw._BaseBuildTarget') + _EXTRA_KEYS_WARNING = ( "This may (unlikely) be an error in the cargo manifest, or may be a missing " @@ -61,15 +63,15 @@ def fixup_meson_varname(name: str) -> str: # Pylance can figure out that these do not, in fact, overlap, but mypy can't @T.overload -def _fixup_raw_mappings(d: manifest.BuildTarget) -> manifest.FixedBuildTarget: ... # type: ignore +def _fixup_raw_mappings(d: raw.BuildTarget) -> manifest.FixedBuildTarget: ... # type: ignore @T.overload -def _fixup_raw_mappings(d: manifest.LibTarget) -> manifest.FixedLibTarget: ... # type: ignore +def _fixup_raw_mappings(d: raw.LibTarget) -> manifest.FixedLibTarget: ... # type: ignore @T.overload -def _fixup_raw_mappings(d: manifest.Dependency) -> manifest.FixedDependency: ... +def _fixup_raw_mappings(d: raw.Dependency) -> manifest.FixedDependency: ... -def _fixup_raw_mappings(d: T.Union[manifest.BuildTarget, manifest.LibTarget, manifest.Dependency] +def _fixup_raw_mappings(d: T.Union[raw.BuildTarget, raw.LibTarget, raw.Dependency] ) -> T.Union[manifest.FixedBuildTarget, manifest.FixedLibTarget, manifest.FixedDependency]: """Fixup raw cargo mappings to ones more suitable for python to consume. @@ -153,7 +155,7 @@ class Package: self.api = _version_to_api(self.version) @classmethod - def from_raw(cls, raw: manifest.Package) -> Self: + def from_raw(cls, raw: raw.Package) -> Self: pkg = T.cast('manifest.FixedPackage', {fixup_meson_varname(k): v for k, v in raw.items()}) pkg = _handle_unknown_keys(pkg, cls, f'Package entry {pkg["name"]}') @@ -233,7 +235,7 @@ class Dependency: raise MesonException(f'Cannot determine minimum API version from {self.version}.') @classmethod - def from_raw(cls, name: str, raw: manifest.DependencyV) -> Dependency: + def from_raw(cls, name: str, raw: raw.DependencyV) -> Dependency: """Create a dependency from a raw cargo dictionary""" if isinstance(raw, str): return cls(name, version.convert(raw)) @@ -242,7 +244,7 @@ class Dependency: @dataclasses.dataclass -class BuildTarget: +class BuildTarget(T.Generic[_R]): name: str crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib']) @@ -270,13 +272,13 @@ class BuildTarget: plugin: bool = False @classmethod - def from_raw(cls, raw: manifest.BuildTarget) -> Self: + def from_raw(cls, raw: raw.BuildTarget) -> Self: name = raw.get('name', '<anonymous>') build = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Binary entry {name}') return cls(**build) @dataclasses.dataclass -class Library(BuildTarget): +class Library(BuildTarget['raw.LibTarget']): """Representation of a Cargo Library Entry.""" @@ -288,20 +290,20 @@ class Library(BuildTarget): doc_scrape_examples: bool = True @classmethod - def from_raw(cls, raw: manifest.LibTarget, fallback_name: str) -> Self: # type: ignore[override] + def from_raw(cls, raw: raw.LibTarget, fallback_name: str) -> Self: # type: ignore[override] + if 'name' not in raw: + raw['name'] = fallback_name fixed = _fixup_raw_mappings(raw) # We need to set the name field if it's not set manually, including if # other fields are set in the lib section - if 'name' not in fixed: - fixed['name'] = fallback_name fixed = _handle_unknown_keys(fixed, cls, f'Library entry {fixed["name"]}') return cls(**fixed) @dataclasses.dataclass -class Binary(BuildTarget): +class Binary(BuildTarget['raw.BuildTarget']): """Representation of a Cargo Bin Entry.""" @@ -309,7 +311,7 @@ class Binary(BuildTarget): @dataclasses.dataclass -class Test(BuildTarget): +class Test(BuildTarget['raw.BuildTarget']): """Representation of a Cargo Test Entry.""" @@ -317,7 +319,7 @@ class Test(BuildTarget): @dataclasses.dataclass -class Benchmark(BuildTarget): +class Benchmark(BuildTarget['raw.BuildTarget']): """Representation of a Cargo Benchmark Entry.""" @@ -325,7 +327,7 @@ class Benchmark(BuildTarget): @dataclasses.dataclass -class Example(BuildTarget): +class Example(BuildTarget['raw.BuildTarget']): """Representation of a Cargo Example Entry.""" @@ -365,7 +367,7 @@ class Manifest: 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: raw.Manifest, subdir: str, path: str = '') -> Manifest: return Manifest( Package.from_raw(raw_manifest['package']), {k: Dependency.from_raw(k, v) for k, v in raw_manifest.get('dependencies', {}).items()}, @@ -503,9 +505,9 @@ class Interpreter: manifest_ = self.manifests.get(subdir) if not manifest_: filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml') - raw = load_toml(filename) - if 'package' in raw: - raw_manifest = T.cast('manifest.Manifest', raw) + toml = load_toml(filename) + if 'package' in toml: + raw_manifest = T.cast('raw.Manifest', toml) manifest_ = _convert_manifest(raw_manifest, subdir) self.manifests[subdir] = manifest_ else: @@ -821,7 +823,7 @@ def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition filename = os.path.join(source_dir, 'Cargo.lock') if os.path.exists(filename): try: - cargolock = T.cast('manifest.CargoLock', load_toml(filename)) + cargolock = T.cast('raw.CargoLock', load_toml(filename)) except TomlImplementationMissing as e: mlog.warning('Failed to load Cargo.lock:', str(e), fatal=False) return wraps diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index d95df7f4f..753979608 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -6,42 +6,9 @@ from __future__ import annotations import typing as T -from typing_extensions import Literal, TypedDict, Required - -EDITION = Literal['2015', '2018', '2021'] -CRATE_TYPE = Literal['bin', 'lib', 'dylib', 'staticlib', 'cdylib', 'rlib', 'proc-macro'] - -Package = TypedDict( - 'Package', - { - 'name': Required[str], - 'version': Required[str], - 'authors': T.List[str], - 'edition': EDITION, - 'rust-version': str, - 'description': str, - 'readme': str, - 'license': str, - 'license-file': str, - 'keywords': T.List[str], - 'categories': T.List[str], - 'workspace': str, - 'build': str, - 'links': str, - 'include': T.List[str], - 'exclude': T.List[str], - 'publish': bool, - 'metadata': T.Dict[str, T.Dict[str, str]], - 'default-run': str, - 'autolib': bool, - 'autobins': bool, - 'autoexamples': bool, - 'autotests': bool, - 'autobenches': bool, - }, - total=False, -) -"""A description of the Package Dictionary.""" +from typing_extensions import TypedDict, Required + +from .raw import EDITION, CRATE_TYPE class FixedPackage(TypedDict, total=False): @@ -73,32 +40,6 @@ class FixedPackage(TypedDict, total=False): autobenches: bool -class Badge(TypedDict): - - """An entry in the badge section.""" - - status: Literal['actively-developed', 'passively-developed', 'as-is', 'experimental', 'deprecated', 'none'] - - -Dependency = TypedDict( - 'Dependency', - { - 'version': str, - 'registry': str, - 'git': str, - 'branch': str, - 'rev': str, - 'path': str, - 'optional': bool, - 'package': str, - 'default-features': bool, - 'features': T.List[str], - }, - total=False, -) -"""An entry in the *dependencies sections.""" - - class FixedDependency(TypedDict, total=False): """An entry in the *dependencies sections, fixed up.""" @@ -115,38 +56,6 @@ class FixedDependency(TypedDict, total=False): features: T.List[str] -DependencyV = T.Union[Dependency, str] -"""A Dependency entry, either a string or a Dependency Dict.""" - - -_BaseBuildTarget = TypedDict( - '_BaseBuildTarget', - { - 'path': str, - 'test': bool, - 'doctest': bool, - 'bench': bool, - 'doc': bool, - 'plugin': bool, - 'proc-macro': bool, - 'harness': bool, - 'edition': EDITION, - 'crate-type': T.List[CRATE_TYPE], - 'required-features': T.List[str], - }, - total=False, -) - - -class BuildTarget(_BaseBuildTarget, total=False): - - name: Required[str] - -class LibTarget(_BaseBuildTarget, total=False): - - name: str - - class _BaseFixedBuildTarget(TypedDict, total=False): path: str test: bool @@ -162,86 +71,10 @@ class _BaseFixedBuildTarget(TypedDict, total=False): class FixedBuildTarget(_BaseFixedBuildTarget, total=False): - name: str - -class FixedLibTarget(_BaseFixedBuildTarget, total=False): - name: Required[str] - proc_macro: bool - -class Target(TypedDict): - """Target entry in the Manifest File.""" - - dependencies: T.Dict[str, DependencyV] - - -class Workspace(TypedDict): - - """The representation of a workspace. - - In a vritual manifest the :attribute:`members` is always present, but in a - project manifest, an empty workspace may be provided, in which case the - workspace is implicitly filled in by values from the path based dependencies. - - the :attribute:`exclude` is always optional - """ - - members: T.List[str] - exclude: T.List[str] - - -Manifest = TypedDict( - 'Manifest', - { - 'package': Required[Package], - 'badges': T.Dict[str, Badge], - 'dependencies': T.Dict[str, DependencyV], - 'dev-dependencies': T.Dict[str, DependencyV], - 'build-dependencies': T.Dict[str, DependencyV], - 'lib': LibTarget, - 'bin': T.List[BuildTarget], - 'test': T.List[BuildTarget], - 'bench': T.List[BuildTarget], - 'example': T.List[BuildTarget], - 'features': T.Dict[str, T.List[str]], - 'target': T.Dict[str, Target], - 'workspace': Workspace, - - # TODO: patch? - # TODO: replace? - }, - total=False, -) -"""The Cargo Manifest format.""" - - -class VirtualManifest(TypedDict): - - """The Representation of a virtual manifest. - - Cargo allows a root manifest that contains only a workspace, this is called - a virtual manifest. This doesn't really map 1:1 with any meson concept, - except perhaps the proposed "meta project". - """ - - workspace: Workspace - -class CargoLockPackage(TypedDict, total=False): - - """A description of a package in the Cargo.lock file format.""" +class FixedLibTarget(_BaseFixedBuildTarget, total=False): name: str - version: str - source: str - checksum: str - - -class CargoLock(TypedDict, total=False): - - """A description of the Cargo.lock file format.""" - - version: str - package: T.List[CargoLockPackage] - metadata: T.Dict[str, str] + proc_macro: bool diff --git a/mesonbuild/cargo/raw.py b/mesonbuild/cargo/raw.py new file mode 100644 index 000000000..f20fc0ef7 --- /dev/null +++ b/mesonbuild/cargo/raw.py @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2022-2024 Intel Corporation + +"""Type definitions for cargo manifest files.""" + +from __future__ import annotations +import typing as T + +from typing_extensions import Literal, TypedDict, Required + +EDITION = Literal['2015', '2018', '2021'] +CRATE_TYPE = Literal['bin', 'lib', 'dylib', 'staticlib', 'cdylib', 'rlib', 'proc-macro'] + +Package = TypedDict( + 'Package', + { + 'name': Required[str], + 'version': Required[str], + 'authors': T.List[str], + 'edition': EDITION, + 'rust-version': str, + 'description': str, + 'readme': str, + 'license': str, + 'license-file': str, + 'keywords': T.List[str], + 'categories': T.List[str], + 'workspace': str, + 'build': str, + 'links': str, + 'include': T.List[str], + 'exclude': T.List[str], + 'publish': bool, + 'metadata': T.Dict[str, T.Dict[str, str]], + 'default-run': str, + 'autolib': bool, + 'autobins': bool, + 'autoexamples': bool, + 'autotests': bool, + 'autobenches': bool, + }, + total=False, +) +"""A description of the Package Dictionary.""" + +class Badge(TypedDict): + + """An entry in the badge section.""" + + status: Literal['actively-developed', 'passively-developed', 'as-is', 'experimental', 'deprecated', 'none'] + + +Dependency = TypedDict( + 'Dependency', + { + 'version': str, + 'registry': str, + 'git': str, + 'branch': str, + 'rev': str, + 'path': str, + 'optional': bool, + 'package': str, + 'default-features': bool, + 'features': T.List[str], + }, + total=False, +) +"""An entry in the *dependencies sections.""" + + +DependencyV = T.Union[Dependency, str] +"""A Dependency entry, either a string or a Dependency Dict.""" + + +_BaseBuildTarget = TypedDict( + '_BaseBuildTarget', + { + 'path': str, + 'test': bool, + 'doctest': bool, + 'bench': bool, + 'doc': bool, + 'plugin': bool, + 'proc-macro': bool, + 'harness': bool, + 'edition': EDITION, + 'crate-type': T.List[CRATE_TYPE], + 'required-features': T.List[str], + }, + total=False, +) + + +class BuildTarget(_BaseBuildTarget, total=False): + + name: Required[str] + + +class LibTarget(_BaseBuildTarget, total=False): + + name: str + + +class Target(TypedDict): + + """Target entry in the Manifest File.""" + + dependencies: T.Dict[str, DependencyV] + + +class Workspace(TypedDict): + + """The representation of a workspace. + + In a vritual manifest the :attribute:`members` is always present, but in a + project manifest, an empty workspace may be provided, in which case the + workspace is implicitly filled in by values from the path based dependencies. + + the :attribute:`exclude` is always optional + """ + + members: T.List[str] + exclude: T.List[str] + + +Manifest = TypedDict( + 'Manifest', + { + 'package': Required[Package], + 'badges': T.Dict[str, Badge], + 'dependencies': T.Dict[str, DependencyV], + 'dev-dependencies': T.Dict[str, DependencyV], + 'build-dependencies': T.Dict[str, DependencyV], + 'lib': LibTarget, + 'bin': T.List[BuildTarget], + 'test': T.List[BuildTarget], + 'bench': T.List[BuildTarget], + 'example': T.List[BuildTarget], + 'features': T.Dict[str, T.List[str]], + 'target': T.Dict[str, Target], + 'workspace': Workspace, + + # TODO: patch? + # TODO: replace? + }, + total=False, +) +"""The Cargo Manifest format.""" + + +class VirtualManifest(TypedDict): + + """The Representation of a virtual manifest. + + Cargo allows a root manifest that contains only a workspace, this is called + a virtual manifest. This doesn't really map 1:1 with any meson concept, + except perhaps the proposed "meta project". + """ + + workspace: Workspace + +class CargoLockPackage(TypedDict, total=False): + + """A description of a package in the Cargo.lock file format.""" + + name: str + version: str + source: str + checksum: str + + +class CargoLock(TypedDict, total=False): + + """A description of the Cargo.lock file format.""" + + version: int + package: T.List[CargoLockPackage] + metadata: T.Dict[str, str] |
