kivyforge 3.0.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kivyforge/__init__.py +9 -0
- kivyforge/artifacts/__init__.py +41 -0
- kivyforge/artifacts/cache.py +81 -0
- kivyforge/artifacts/collect.py +354 -0
- kivyforge/artifacts/download.py +125 -0
- kivyforge/artifacts/frameworks.py +240 -0
- kivyforge/artifacts/native_stage_util.py +346 -0
- kivyforge/artifacts/runtime.py +65 -0
- kivyforge/artifacts/verify.py +45 -0
- kivyforge/artifacts/wheels.py +89 -0
- kivyforge/bundle/__init__.py +1 -0
- kivyforge/bundle/pycompile.py +161 -0
- kivyforge/cli/__init__.py +52 -0
- kivyforge/cli/_common.py +62 -0
- kivyforge/cli/_platform.py +84 -0
- kivyforge/cli/build.py +111 -0
- kivyforge/cli/clean.py +135 -0
- kivyforge/cli/doctor.py +52 -0
- kivyforge/cli/init.py +612 -0
- kivyforge/cli/init_writer.py +716 -0
- kivyforge/cli/lock.py +261 -0
- kivyforge/cli/open_cmd.py +15 -0
- kivyforge/cli/package.py +134 -0
- kivyforge/cli/run.py +162 -0
- kivyforge/cli/status.py +20 -0
- kivyforge/cli/upgrade.py +314 -0
- kivyforge/config/__init__.py +46 -0
- kivyforge/config/errors.py +43 -0
- kivyforge/config/icons.py +60 -0
- kivyforge/config/loader.py +3079 -0
- kivyforge/config/model.py +817 -0
- kivyforge/doctor/__init__.py +19 -0
- kivyforge/doctor/checks_common.py +95 -0
- kivyforge/doctor/probe.py +357 -0
- kivyforge/doctor/result.py +41 -0
- kivyforge/lock/__init__.py +42 -0
- kivyforge/lock/_pip_shim.py +63 -0
- kivyforge/lock/find_links.py +182 -0
- kivyforge/lock/model.py +69 -0
- kivyforge/lock/pep751.py +111 -0
- kivyforge/lock/reader.py +30 -0
- kivyforge/lock/resolver.py +124 -0
- kivyforge/lock/wheelruntime/__init__.py +70 -0
- kivyforge/lock/wheelruntime/builder.py +280 -0
- kivyforge/lock/wheelruntime/model.py +94 -0
- kivyforge/lock/wheelruntime/native_binaries.py +127 -0
- kivyforge/lock/wheelruntime/pbs_github.py +133 -0
- kivyforge/lock/wheelruntime/profile.py +104 -0
- kivyforge/lock/wheelruntime/resolver.py +248 -0
- kivyforge/lock/wheelruntime/runtime.py +161 -0
- kivyforge/lock/wheelruntime/serialize.py +245 -0
- kivyforge/platforms/__init__.py +114 -0
- kivyforge/platforms/android/__init__.py +140 -0
- kivyforge/platforms/android/adb.py +215 -0
- kivyforge/platforms/android/bootstrap/__init__.py +7 -0
- kivyforge/platforms/android/bootstrap/contract.py +166 -0
- kivyforge/platforms/android/bootstrap/render.py +244 -0
- kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
- kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
- kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
- kivyforge/platforms/android/cli.py +1023 -0
- kivyforge/platforms/android/doctor.py +899 -0
- kivyforge/platforms/android/elf.py +148 -0
- kivyforge/platforms/android/generate/__init__.py +1 -0
- kivyforge/platforms/android/generate/manifest.py +261 -0
- kivyforge/platforms/android/generate/project.py +462 -0
- kivyforge/platforms/android/generate/services.py +187 -0
- kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
- kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
- kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
- kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
- kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
- kivyforge/platforms/android/gradlew.py +60 -0
- kivyforge/platforms/android/icons.py +237 -0
- kivyforge/platforms/android/lock/__init__.py +1 -0
- kivyforge/platforms/android/lock/archives.py +109 -0
- kivyforge/platforms/android/lock/builder.py +387 -0
- kivyforge/platforms/android/lock/markers.py +71 -0
- kivyforge/platforms/android/lock/maven.py +252 -0
- kivyforge/platforms/android/lock/model.py +113 -0
- kivyforge/platforms/android/lock/python_meta.py +199 -0
- kivyforge/platforms/android/lock/reader.py +206 -0
- kivyforge/platforms/android/lock/resolver.py +276 -0
- kivyforge/platforms/android/lock/writer.py +121 -0
- kivyforge/platforms/android/policy.py +214 -0
- kivyforge/platforms/android/signing.py +145 -0
- kivyforge/platforms/android/smoke.py +43 -0
- kivyforge/platforms/android/splash.py +293 -0
- kivyforge/platforms/android/stage/__init__.py +1 -0
- kivyforge/platforms/android/stage/bundle.py +295 -0
- kivyforge/platforms/android/stage/jnilibs.py +186 -0
- kivyforge/platforms/android/stage/runtime.py +61 -0
- kivyforge/platforms/android/stage/wheels.py +106 -0
- kivyforge/platforms/android/toolchain.py +29 -0
- kivyforge/platforms/base.py +157 -0
- kivyforge/platforms/ios/__init__.py +129 -0
- kivyforge/platforms/ios/assets.py +193 -0
- kivyforge/platforms/ios/buildsettings.py +128 -0
- kivyforge/platforms/ios/cli.py +543 -0
- kivyforge/platforms/ios/doctor.py +410 -0
- kivyforge/platforms/ios/entitlements.py +34 -0
- kivyforge/platforms/ios/generator.py +318 -0
- kivyforge/platforms/ios/lock/__init__.py +71 -0
- kivyforge/platforms/ios/lock/builder.py +393 -0
- kivyforge/platforms/ios/lock/markers.py +69 -0
- kivyforge/platforms/ios/lock/model.py +95 -0
- kivyforge/platforms/ios/lock/python_meta.py +187 -0
- kivyforge/platforms/ios/lock/reader.py +191 -0
- kivyforge/platforms/ios/lock/resolver.py +304 -0
- kivyforge/platforms/ios/lock/spm.py +217 -0
- kivyforge/platforms/ios/lock/writer.py +114 -0
- kivyforge/platforms/ios/lock/xcframework.py +188 -0
- kivyforge/platforms/ios/materialize.py +59 -0
- kivyforge/platforms/ios/plist.py +91 -0
- kivyforge/platforms/ios/privacy.py +39 -0
- kivyforge/platforms/ios/skeleton.py +165 -0
- kivyforge/platforms/ios/sources.py +63 -0
- kivyforge/platforms/ios/staging.py +225 -0
- kivyforge/platforms/ios/swift_packages.py +324 -0
- kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
- kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
- kivyforge/platforms/ios/templates/main.m +15 -0
- kivyforge/platforms/ios/templates/main_config.h.in +12 -0
- kivyforge/platforms/ios/xcframeworks.py +194 -0
- kivyforge/platforms/ios/xcode/__init__.py +76 -0
- kivyforge/platforms/ios/xcode/commands.py +296 -0
- kivyforge/platforms/ios/xcode/runner.py +316 -0
- kivyforge/platforms/linux/__init__.py +120 -0
- kivyforge/platforms/linux/appimage.py +169 -0
- kivyforge/platforms/linux/bundle.py +235 -0
- kivyforge/platforms/linux/cli.py +232 -0
- kivyforge/platforms/linux/desktop.py +71 -0
- kivyforge/platforms/linux/doctor.py +438 -0
- kivyforge/platforms/linux/elftools.py +95 -0
- kivyforge/platforms/linux/icons.py +129 -0
- kivyforge/platforms/linux/launcher.py +107 -0
- kivyforge/platforms/linux/lock/__init__.py +126 -0
- kivyforge/platforms/linux/lock/profile.py +254 -0
- kivyforge/platforms/linux/lock/runtime.py +75 -0
- kivyforge/platforms/linux/native_stage.py +47 -0
- kivyforge/platforms/linux/runtime_stage.py +107 -0
- kivyforge/platforms/linux/wheels_stage.py +103 -0
- kivyforge/platforms/macos/__init__.py +121 -0
- kivyforge/platforms/macos/bundle.py +255 -0
- kivyforge/platforms/macos/cli.py +269 -0
- kivyforge/platforms/macos/doctor.py +480 -0
- kivyforge/platforms/macos/icns.py +75 -0
- kivyforge/platforms/macos/launcher.py +118 -0
- kivyforge/platforms/macos/lock/__init__.py +112 -0
- kivyforge/platforms/macos/lock/profile.py +119 -0
- kivyforge/platforms/macos/lock/runtime.py +58 -0
- kivyforge/platforms/macos/machotools.py +138 -0
- kivyforge/platforms/macos/native_stage.py +47 -0
- kivyforge/platforms/macos/notarize.py +143 -0
- kivyforge/platforms/macos/plist.py +37 -0
- kivyforge/platforms/macos/runtime_stage.py +110 -0
- kivyforge/platforms/macos/signing.py +78 -0
- kivyforge/platforms/macos/wheels_stage.py +108 -0
- kivyforge/platforms/windows/__init__.py +133 -0
- kivyforge/platforms/windows/assets.py +76 -0
- kivyforge/platforms/windows/bundle.py +298 -0
- kivyforge/platforms/windows/cli.py +314 -0
- kivyforge/platforms/windows/doctor.py +563 -0
- kivyforge/platforms/windows/fsswap.py +87 -0
- kivyforge/platforms/windows/icons.py +64 -0
- kivyforge/platforms/windows/launcher/__init__.py +193 -0
- kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
- kivyforge/platforms/windows/launcher/launcher.c +379 -0
- kivyforge/platforms/windows/lock/__init__.py +112 -0
- kivyforge/platforms/windows/lock/profile.py +101 -0
- kivyforge/platforms/windows/lock/runtime.py +64 -0
- kivyforge/platforms/windows/naming.py +52 -0
- kivyforge/platforms/windows/native_stage.py +66 -0
- kivyforge/platforms/windows/petools.py +98 -0
- kivyforge/platforms/windows/rcedit.py +116 -0
- kivyforge/platforms/windows/runtime_stage.py +221 -0
- kivyforge/platforms/windows/signing.py +168 -0
- kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
- kivyforge/platforms/windows/vendor/NOTICE +17 -0
- kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
- kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
- kivyforge/platforms/windows/vendor/__init__.py +12 -0
- kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
- kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
- kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
- kivyforge/platforms/windows/wheels_stage.py +139 -0
- kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
- kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
- kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
- kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
- kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
- kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Serialize/parse ``pylock.<platform>.toml`` for the wheel+runtime family.
|
|
2
|
+
|
|
3
|
+
Reuses the platform-neutral PEP 751 ``[[packages]]`` emitter/parser
|
|
4
|
+
(``kivyforge.lock.pep751``) and adds the ``[tool.kivyforge]`` extension: the arch
|
|
5
|
+
set plus the bundled Python runtime pin (provider + per-arch artifacts). The
|
|
6
|
+
platform is carried by the filename, so ``loads``/``load`` take it explicitly
|
|
7
|
+
(used only for clear error messages and to stamp the model).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import tomllib
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ..model import LOCK_VERSION
|
|
16
|
+
from ..pep751 import arr as _arr
|
|
17
|
+
from ..pep751 import emit_package as _emit_package
|
|
18
|
+
from ..pep751 import parse_package as _parse_package
|
|
19
|
+
from ..pep751 import s as _s
|
|
20
|
+
from ..reader import LockError
|
|
21
|
+
from .model import (
|
|
22
|
+
TOOL_SCHEMA_VERSION,
|
|
23
|
+
LockedNativeBinary,
|
|
24
|
+
PythonRuntime,
|
|
25
|
+
RuntimeArtifact,
|
|
26
|
+
WheelRuntimeLock,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
SUPPORTED_LOCK_VERSION_MAJOR = int(LOCK_VERSION.split(".", 1)[0])
|
|
30
|
+
SUPPORTED_TOOL_SCHEMA_VERSION = TOOL_SCHEMA_VERSION
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def dumps(lock: WheelRuntimeLock) -> str:
|
|
34
|
+
lines: list[str] = []
|
|
35
|
+
|
|
36
|
+
lines.append(f"lock-version = {_s(lock.lock_version)}")
|
|
37
|
+
lines.append(f"created-by = {_s(lock.created_by)}")
|
|
38
|
+
lines.append(f"requires-python = {_s(lock.requires_python)}")
|
|
39
|
+
lines.append(f"extras = {_arr(lock.extras)}")
|
|
40
|
+
lines.append(f"dependency-groups = {_arr(lock.dependency_groups)}")
|
|
41
|
+
lines.append(f"default-groups = {_arr(lock.default_groups)}")
|
|
42
|
+
lines.append("")
|
|
43
|
+
|
|
44
|
+
for pkg in sorted(lock.packages, key=lambda p: p.sort_key):
|
|
45
|
+
_emit_package(lines, pkg)
|
|
46
|
+
|
|
47
|
+
lines.append("[tool.kivyforge]")
|
|
48
|
+
lines.append(f"schema_version = {lock.schema_version}")
|
|
49
|
+
lines.append(f"kivyforge_version = {_s(lock.kivyforge_version)}")
|
|
50
|
+
lines.append(f"generated_at = {_s(lock.generated_at)}")
|
|
51
|
+
lines.append(f"pyproject_sha256 = {_s(lock.pyproject_sha256)}")
|
|
52
|
+
lines.append(
|
|
53
|
+
f"tool_kivyforge_schema_version = {lock.tool_kivyforge_schema_version}"
|
|
54
|
+
)
|
|
55
|
+
lines.append(f"archs = {_arr(lock.archs)}")
|
|
56
|
+
lines.append("")
|
|
57
|
+
lines.append("[tool.kivyforge.python_runtime]")
|
|
58
|
+
lines.append(f"provider = {_s(lock.python_runtime.provider)}")
|
|
59
|
+
lines.append(f"version = {_s(lock.python_runtime.version)}")
|
|
60
|
+
if lock.python_runtime.floor:
|
|
61
|
+
lines.append(f"floor = {_s(lock.python_runtime.floor)}")
|
|
62
|
+
|
|
63
|
+
for art in sorted(lock.python_runtime.artifacts, key=lambda a: a.arch):
|
|
64
|
+
lines.append("")
|
|
65
|
+
lines.append("[[tool.kivyforge.python_runtime.artifacts]]")
|
|
66
|
+
lines.append(f"arch = {_s(art.arch)}")
|
|
67
|
+
lines.append(f"url = {_s(art.url)}")
|
|
68
|
+
lines.append(f"sha256 = {_s(art.sha256)}")
|
|
69
|
+
lines.append(f"archive_format = {_s(art.archive_format)}")
|
|
70
|
+
|
|
71
|
+
for nb in sorted(lock.native_binaries, key=lambda b: b.name):
|
|
72
|
+
lines.append("")
|
|
73
|
+
lines.append("[[tool.kivyforge.native_binaries]]")
|
|
74
|
+
lines.append(f"name = {_s(nb.name)}")
|
|
75
|
+
lines.append(f"version = {_s(nb.version)}")
|
|
76
|
+
if nb.url is not None:
|
|
77
|
+
lines.append(f"url = {_s(nb.url)}")
|
|
78
|
+
else:
|
|
79
|
+
lines.append(f"path = {_s(nb.path)}")
|
|
80
|
+
lines.append(f"sha256 = {_s(nb.sha256)}")
|
|
81
|
+
|
|
82
|
+
return "\n".join(lines) + "\n"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def loads(text: str, *, platform: str) -> WheelRuntimeLock:
|
|
86
|
+
name = f"pylock.{platform}.toml"
|
|
87
|
+
try:
|
|
88
|
+
raw = tomllib.loads(text)
|
|
89
|
+
except tomllib.TOMLDecodeError as exc:
|
|
90
|
+
raise LockError(f"{name} is not valid TOML: {exc}") from exc
|
|
91
|
+
if not isinstance(raw, dict):
|
|
92
|
+
raise LockError(f"{name} must be a TOML table.")
|
|
93
|
+
try:
|
|
94
|
+
return _from_raw(raw, platform=platform, name=name)
|
|
95
|
+
except LockError:
|
|
96
|
+
raise
|
|
97
|
+
except (KeyError, TypeError, AttributeError, ValueError) as exc:
|
|
98
|
+
raise LockError(f"{name} is malformed: {exc!r}") from exc
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def load(path: str | Path, *, platform: str) -> WheelRuntimeLock:
|
|
102
|
+
return loads(Path(path).read_text(encoding="utf-8"), platform=platform)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _from_raw(raw: dict, *, platform: str, name: str) -> WheelRuntimeLock:
|
|
106
|
+
lock_version = _check_lock_version(raw, name)
|
|
107
|
+
|
|
108
|
+
tool_raw = raw.get("tool", {})
|
|
109
|
+
if not isinstance(tool_raw, dict):
|
|
110
|
+
raise LockError(f"{name} [tool] must be a table.")
|
|
111
|
+
tool = tool_raw.get("kivyforge", {})
|
|
112
|
+
if not isinstance(tool, dict) or not tool:
|
|
113
|
+
raise LockError(f"{name} is missing the [tool.kivyforge] table.")
|
|
114
|
+
|
|
115
|
+
schema_version = _check_tool_schema_version(tool)
|
|
116
|
+
runtime = _parse_runtime(tool, name)
|
|
117
|
+
native_binaries = _parse_native_binaries(tool, name)
|
|
118
|
+
|
|
119
|
+
packages = tuple(_parse_package(p) for p in _as_list(raw, "packages", name))
|
|
120
|
+
|
|
121
|
+
return WheelRuntimeLock(
|
|
122
|
+
platform=platform,
|
|
123
|
+
requires_python=raw.get("requires-python", ">=3.15"),
|
|
124
|
+
packages=packages,
|
|
125
|
+
python_runtime=runtime,
|
|
126
|
+
archs=tuple(tool.get("archs", [])),
|
|
127
|
+
kivyforge_version=tool.get("kivyforge_version", ""),
|
|
128
|
+
generated_at=tool.get("generated_at", ""),
|
|
129
|
+
pyproject_sha256=tool.get("pyproject_sha256", ""),
|
|
130
|
+
tool_kivyforge_schema_version=tool.get("tool_kivyforge_schema_version", 1),
|
|
131
|
+
schema_version=schema_version,
|
|
132
|
+
lock_version=lock_version,
|
|
133
|
+
created_by=raw.get("created-by", "kivyforge"),
|
|
134
|
+
extras=tuple(raw.get("extras", [])),
|
|
135
|
+
dependency_groups=tuple(raw.get("dependency-groups", [])),
|
|
136
|
+
default_groups=tuple(raw.get("default-groups", [])),
|
|
137
|
+
native_binaries=native_binaries,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _check_lock_version(raw: dict, name: str) -> str:
|
|
142
|
+
lock_version = raw.get("lock-version", LOCK_VERSION)
|
|
143
|
+
if not isinstance(lock_version, str):
|
|
144
|
+
raise LockError(f"{name} lock-version must be a string.")
|
|
145
|
+
major = lock_version.split(".", 1)[0]
|
|
146
|
+
if not major.isdigit():
|
|
147
|
+
raise LockError(f"{name} lock-version {lock_version!r} is not a valid version.")
|
|
148
|
+
if int(major) > SUPPORTED_LOCK_VERSION_MAJOR:
|
|
149
|
+
raise LockError(
|
|
150
|
+
f"{name} lock-version {lock_version} is newer than this kivyforge "
|
|
151
|
+
f"understands (max {SUPPORTED_LOCK_VERSION_MAJOR}.x). Upgrade kivyforge."
|
|
152
|
+
)
|
|
153
|
+
return lock_version
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _check_tool_schema_version(tool: dict) -> int:
|
|
157
|
+
schema_version = tool.get("schema_version", SUPPORTED_TOOL_SCHEMA_VERSION)
|
|
158
|
+
if not isinstance(schema_version, int) or isinstance(schema_version, bool):
|
|
159
|
+
raise LockError("[tool.kivyforge].schema_version must be an integer.")
|
|
160
|
+
if schema_version > SUPPORTED_TOOL_SCHEMA_VERSION:
|
|
161
|
+
raise LockError(
|
|
162
|
+
f"[tool.kivyforge].schema_version {schema_version} is newer than this "
|
|
163
|
+
f"kivyforge understands (max {SUPPORTED_TOOL_SCHEMA_VERSION}). "
|
|
164
|
+
"Upgrade kivyforge."
|
|
165
|
+
)
|
|
166
|
+
return schema_version
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _parse_runtime(tool: dict, name: str) -> PythonRuntime:
|
|
170
|
+
rt = tool.get("python_runtime")
|
|
171
|
+
if not isinstance(rt, dict) or not rt:
|
|
172
|
+
raise LockError(f"{name} is missing [tool.kivyforge.python_runtime].")
|
|
173
|
+
for field in ("provider", "version"):
|
|
174
|
+
value = rt.get(field)
|
|
175
|
+
if not isinstance(value, str) or not value:
|
|
176
|
+
raise LockError(
|
|
177
|
+
f"[tool.kivyforge.python_runtime].{field} is missing or empty."
|
|
178
|
+
)
|
|
179
|
+
artifacts_raw = rt.get("artifacts", [])
|
|
180
|
+
if not isinstance(artifacts_raw, list) or not artifacts_raw:
|
|
181
|
+
raise LockError(
|
|
182
|
+
"[tool.kivyforge.python_runtime] must list at least one artifact."
|
|
183
|
+
)
|
|
184
|
+
artifacts = []
|
|
185
|
+
for a in artifacts_raw:
|
|
186
|
+
for field in ("arch", "url", "sha256"):
|
|
187
|
+
if not isinstance(a.get(field), str) or not a.get(field):
|
|
188
|
+
raise LockError(
|
|
189
|
+
f"a [tool.kivyforge.python_runtime.artifacts] entry is "
|
|
190
|
+
f"missing {field!r}."
|
|
191
|
+
)
|
|
192
|
+
artifacts.append(
|
|
193
|
+
RuntimeArtifact(
|
|
194
|
+
arch=a["arch"],
|
|
195
|
+
url=a["url"],
|
|
196
|
+
sha256=a["sha256"],
|
|
197
|
+
archive_format=a.get("archive_format", "tar.gz"),
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
return PythonRuntime(
|
|
201
|
+
provider=rt["provider"],
|
|
202
|
+
version=rt["version"],
|
|
203
|
+
artifacts=tuple(artifacts),
|
|
204
|
+
floor=rt.get("floor"),
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _parse_native_binaries(tool: dict, name: str) -> tuple[LockedNativeBinary, ...]:
|
|
209
|
+
raw = tool.get("native_binaries", [])
|
|
210
|
+
if not isinstance(raw, list):
|
|
211
|
+
raise LockError(f"{name} [tool.kivyforge.native_binaries] must be an array.")
|
|
212
|
+
out: list[LockedNativeBinary] = []
|
|
213
|
+
for entry in raw:
|
|
214
|
+
if not isinstance(entry, dict):
|
|
215
|
+
raise LockError(
|
|
216
|
+
f"{name} [[tool.kivyforge.native_binaries]] entries must be tables."
|
|
217
|
+
)
|
|
218
|
+
for field in ("name", "version", "sha256"):
|
|
219
|
+
if not isinstance(entry.get(field), str) or not entry.get(field):
|
|
220
|
+
raise LockError(
|
|
221
|
+
f"a [[tool.kivyforge.native_binaries]] entry is missing {field!r}."
|
|
222
|
+
)
|
|
223
|
+
url = entry.get("url")
|
|
224
|
+
path = entry.get("path")
|
|
225
|
+
if bool(url) == bool(path):
|
|
226
|
+
raise LockError(
|
|
227
|
+
f"native binary {entry['name']!r} must have exactly one of url/path."
|
|
228
|
+
)
|
|
229
|
+
out.append(
|
|
230
|
+
LockedNativeBinary(
|
|
231
|
+
name=entry["name"],
|
|
232
|
+
version=entry["version"],
|
|
233
|
+
sha256=entry["sha256"],
|
|
234
|
+
url=url,
|
|
235
|
+
path=path,
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
return tuple(sorted(out, key=lambda b: b.name))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _as_list(table: dict, key: str, name: str) -> list:
|
|
242
|
+
value = table.get(key, [])
|
|
243
|
+
if not isinstance(value, list):
|
|
244
|
+
raise LockError(f"{name} {key!r} must be an array of tables.")
|
|
245
|
+
return value
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Platform backend registry + target resolution (common design docs 02, 05).
|
|
2
|
+
|
|
3
|
+
The registry is a plain, static dict of in-repo backends — no entry-point plugin
|
|
4
|
+
discovery yet (it can be added later without changing callers). ``resolve_target``
|
|
5
|
+
implements the documented resolution chain:
|
|
6
|
+
|
|
7
|
+
--platform / -p > KIVYFORGE_PLATFORM > host OS > actionable error
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import platform as _platform
|
|
13
|
+
from collections.abc import Collection, Mapping
|
|
14
|
+
|
|
15
|
+
from .android import AndroidPlatform
|
|
16
|
+
from .base import HostCapabilityError, Platform
|
|
17
|
+
from .ios import IosPlatform
|
|
18
|
+
from .linux import LinuxPlatform
|
|
19
|
+
from .macos import MacosPlatform
|
|
20
|
+
from .windows import WindowsPlatform
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"HostCapabilityError",
|
|
24
|
+
"Platform",
|
|
25
|
+
"PlatformResolutionError",
|
|
26
|
+
"PLATFORM_ENV_VAR",
|
|
27
|
+
"available_platform_names",
|
|
28
|
+
"get_platform",
|
|
29
|
+
"resolve_target",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
PLATFORM_ENV_VAR = "KIVYFORGE_PLATFORM"
|
|
33
|
+
|
|
34
|
+
# Registered backends, in display order. Add new platforms here.
|
|
35
|
+
_REGISTRY: tuple[Platform, ...] = (
|
|
36
|
+
IosPlatform(),
|
|
37
|
+
MacosPlatform(),
|
|
38
|
+
LinuxPlatform(),
|
|
39
|
+
WindowsPlatform(),
|
|
40
|
+
AndroidPlatform(),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# name/alias -> backend
|
|
44
|
+
_BY_SELECTOR: dict[str, Platform] = {
|
|
45
|
+
selector: backend for backend in _REGISTRY for selector in backend.selectors
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class PlatformResolutionError(Exception):
|
|
50
|
+
"""No target platform could be resolved from CLI/env/host."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def available_platform_names() -> list[str]:
|
|
54
|
+
"""Canonical names of registered platforms (for ``click.Choice``)."""
|
|
55
|
+
return [backend.name for backend in _REGISTRY]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_platform(name: str) -> Platform:
|
|
59
|
+
"""Return the backend registered under ``name`` or an alias."""
|
|
60
|
+
try:
|
|
61
|
+
return _BY_SELECTOR[name]
|
|
62
|
+
except KeyError:
|
|
63
|
+
raise PlatformResolutionError(
|
|
64
|
+
f"unknown platform {name!r}; "
|
|
65
|
+
f"registered: {', '.join(available_platform_names())}"
|
|
66
|
+
) from None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def resolve_target(
|
|
70
|
+
cli_platform: str | None,
|
|
71
|
+
*,
|
|
72
|
+
configured: Collection[str],
|
|
73
|
+
env: Mapping[str, str] | None = None,
|
|
74
|
+
host_system: str | None = None,
|
|
75
|
+
verb: str = "build",
|
|
76
|
+
) -> Platform:
|
|
77
|
+
"""Resolve the target platform per the documented chain.
|
|
78
|
+
|
|
79
|
+
``configured`` is the set of platform names whose overlay this project's
|
|
80
|
+
``pyproject.toml`` actually declares (e.g. ``{"ios"}`` for ``[tool.kivy.ios]``).
|
|
81
|
+
``env``/``host_system`` are injectable for testing. ``verb`` names the CLI
|
|
82
|
+
command that called this (e.g. ``"lock"``, ``"run"``) purely so the error
|
|
83
|
+
message's example command is accurate; it never affects resolution.
|
|
84
|
+
"""
|
|
85
|
+
env = env if env is not None else {}
|
|
86
|
+
|
|
87
|
+
# 1. explicit --platform / -p
|
|
88
|
+
if cli_platform:
|
|
89
|
+
return get_platform(cli_platform)
|
|
90
|
+
|
|
91
|
+
# 2. KIVYFORGE_PLATFORM session/CI default
|
|
92
|
+
env_platform = env.get(PLATFORM_ENV_VAR)
|
|
93
|
+
if env_platform:
|
|
94
|
+
return get_platform(env_platform)
|
|
95
|
+
|
|
96
|
+
# 3. host OS -> its own platform, only if registered *and* configured
|
|
97
|
+
host = host_system if host_system is not None else _platform.system()
|
|
98
|
+
for backend in _REGISTRY:
|
|
99
|
+
if backend.host_system == host and backend.name in configured:
|
|
100
|
+
return backend
|
|
101
|
+
|
|
102
|
+
# 4. actionable error
|
|
103
|
+
raise PlatformResolutionError(_resolution_help(configured, verb))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _resolution_help(configured: Collection[str], verb: str = "build") -> str:
|
|
107
|
+
configured_list = ", ".join(sorted(configured)) or "none"
|
|
108
|
+
example = sorted(configured)[0] if configured else "macos"
|
|
109
|
+
return (
|
|
110
|
+
"no target platform resolved.\n"
|
|
111
|
+
f" Pass one explicitly: kivyforge {verb} --platform {example}\n"
|
|
112
|
+
f" Or set a session default: export {PLATFORM_ENV_VAR}={example}\n"
|
|
113
|
+
f" (Configured platforms in this pyproject.toml: {configured_list})"
|
|
114
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Android platform backend (docs/design/platforms/android/01-08).
|
|
2
|
+
|
|
3
|
+
Produces a Gradle/AGP project from ``pyproject.toml`` + ``pylock.android.toml``
|
|
4
|
+
— the ABI-independent Python asset bundle, flattened extension ``.so``s in
|
|
5
|
+
``jniLibs/<abi>/``, the kivyforge-owned bootstrap (stock SDL Java glue +
|
|
6
|
+
``PythonActivity`` + the ``SDL_main`` native launcher), and a signed ``.apk`` /
|
|
7
|
+
``.aab``. Android is a cross-compiled target: no host OS maps to it as a
|
|
8
|
+
default, so it always needs explicit ``-p android`` (or ``KIVYFORGE_PLATFORM``).
|
|
9
|
+
Any desktop host can build: the backend consumes prebuilt wheels + the prebuilt
|
|
10
|
+
python.org runtime and drives Gradle/SDK/NDK, all cross-platform.
|
|
11
|
+
|
|
12
|
+
The load model this backend generates was proven on-device before any of this
|
|
13
|
+
code was written — see docs/design/dev/android-loadmodel-findings.md.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from ..base import Platform
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from kivyforge.doctor.result import CheckResult
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AndroidBuildError(Exception):
|
|
28
|
+
"""An Android build/stage failure surfaced with an actionable message."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AndroidPlatform(Platform):
|
|
32
|
+
name = "android"
|
|
33
|
+
aliases = ()
|
|
34
|
+
# Cross-compiled target: never a host default (resolution-chain step 3).
|
|
35
|
+
host_system = None
|
|
36
|
+
# apk = sideload/CI unit (default); aab = Play upload unit.
|
|
37
|
+
package_formats = ("apk", "aab")
|
|
38
|
+
|
|
39
|
+
def check_host_capability(self, *, host_system: str | None = None) -> None:
|
|
40
|
+
# Windows, macOS, and Linux hosts can all build for Android: kivyforge
|
|
41
|
+
# consumes prebuilt wheels + the prebuilt python.org runtime and drives
|
|
42
|
+
# Gradle + the Android SDK/NDK, which are cross-platform (android/06
|
|
43
|
+
# §host support). Host *adequacy* (JDK, SDK, NDK present) is doctor's
|
|
44
|
+
# job, not a capability gate.
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
def build(
|
|
48
|
+
self,
|
|
49
|
+
project_root: Path,
|
|
50
|
+
*,
|
|
51
|
+
target: str | None,
|
|
52
|
+
arch: str | None,
|
|
53
|
+
no_verify_lock: bool,
|
|
54
|
+
no_cache: bool,
|
|
55
|
+
team_id: str | None,
|
|
56
|
+
signing_identity: str | None,
|
|
57
|
+
export_method: str,
|
|
58
|
+
debug: bool = False,
|
|
59
|
+
fmt: str | None = None,
|
|
60
|
+
abi: str | None = None,
|
|
61
|
+
) -> None:
|
|
62
|
+
# `target` carries the iOS-only selectors. Without --debug, build stops
|
|
63
|
+
# after generating the project; with it, Gradle assembles the debug
|
|
64
|
+
# artifact (android/06 step 8). `--abi` is the Android spelling of the
|
|
65
|
+
# ABI restriction; `--arch` is accepted for it too.
|
|
66
|
+
self.reject_ios_only_target(target)
|
|
67
|
+
from .cli import android_build
|
|
68
|
+
|
|
69
|
+
android_build(
|
|
70
|
+
project_root,
|
|
71
|
+
debug=debug,
|
|
72
|
+
fmt=fmt or "apk",
|
|
73
|
+
abi=abi or arch,
|
|
74
|
+
no_verify_lock=no_verify_lock,
|
|
75
|
+
no_cache=no_cache,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def run(
|
|
79
|
+
self,
|
|
80
|
+
project_root: Path,
|
|
81
|
+
*,
|
|
82
|
+
target: str,
|
|
83
|
+
arch: str | None,
|
|
84
|
+
destination: str | None,
|
|
85
|
+
no_build: bool,
|
|
86
|
+
) -> None:
|
|
87
|
+
from .cli import android_run
|
|
88
|
+
|
|
89
|
+
android_run(
|
|
90
|
+
project_root,
|
|
91
|
+
no_build=no_build,
|
|
92
|
+
abi=arch,
|
|
93
|
+
serial=destination,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def package(
|
|
97
|
+
self,
|
|
98
|
+
project_root: Path,
|
|
99
|
+
*,
|
|
100
|
+
fmt: str,
|
|
101
|
+
arch: str | None,
|
|
102
|
+
team_id: str | None,
|
|
103
|
+
signing_identity: str | None,
|
|
104
|
+
export_method: str,
|
|
105
|
+
notarize: bool | None,
|
|
106
|
+
notary_profile: str | None,
|
|
107
|
+
no_verify_lock: bool,
|
|
108
|
+
no_cache: bool,
|
|
109
|
+
abi: str | None = None,
|
|
110
|
+
keystore: str | None = None,
|
|
111
|
+
key_alias: str | None = None,
|
|
112
|
+
) -> None:
|
|
113
|
+
from .cli import android_package
|
|
114
|
+
|
|
115
|
+
android_package(
|
|
116
|
+
project_root,
|
|
117
|
+
fmt=fmt,
|
|
118
|
+
abi=abi or arch,
|
|
119
|
+
keystore=keystore,
|
|
120
|
+
key_alias=key_alias,
|
|
121
|
+
no_verify_lock=no_verify_lock,
|
|
122
|
+
no_cache=no_cache,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def status(self, project_root: Path) -> None:
|
|
126
|
+
from .cli import android_status
|
|
127
|
+
|
|
128
|
+
android_status(project_root)
|
|
129
|
+
|
|
130
|
+
def open_project(self, project_root: Path) -> None:
|
|
131
|
+
from .cli import android_open
|
|
132
|
+
|
|
133
|
+
android_open(project_root)
|
|
134
|
+
|
|
135
|
+
def doctor(
|
|
136
|
+
self, cwd: Path, *, kivyforge_version: str, offline: bool
|
|
137
|
+
) -> list[CheckResult]:
|
|
138
|
+
from .doctor import android_doctor
|
|
139
|
+
|
|
140
|
+
return android_doctor(cwd, kivyforge_version=kivyforge_version, offline=offline)
|