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.
Files changed (225) hide show
  1. kivyforge/__init__.py +9 -0
  2. kivyforge/artifacts/__init__.py +41 -0
  3. kivyforge/artifacts/cache.py +81 -0
  4. kivyforge/artifacts/collect.py +354 -0
  5. kivyforge/artifacts/download.py +125 -0
  6. kivyforge/artifacts/frameworks.py +240 -0
  7. kivyforge/artifacts/native_stage_util.py +346 -0
  8. kivyforge/artifacts/runtime.py +65 -0
  9. kivyforge/artifacts/verify.py +45 -0
  10. kivyforge/artifacts/wheels.py +89 -0
  11. kivyforge/bundle/__init__.py +1 -0
  12. kivyforge/bundle/pycompile.py +161 -0
  13. kivyforge/cli/__init__.py +52 -0
  14. kivyforge/cli/_common.py +62 -0
  15. kivyforge/cli/_platform.py +84 -0
  16. kivyforge/cli/build.py +111 -0
  17. kivyforge/cli/clean.py +135 -0
  18. kivyforge/cli/doctor.py +52 -0
  19. kivyforge/cli/init.py +612 -0
  20. kivyforge/cli/init_writer.py +716 -0
  21. kivyforge/cli/lock.py +261 -0
  22. kivyforge/cli/open_cmd.py +15 -0
  23. kivyforge/cli/package.py +134 -0
  24. kivyforge/cli/run.py +162 -0
  25. kivyforge/cli/status.py +20 -0
  26. kivyforge/cli/upgrade.py +314 -0
  27. kivyforge/config/__init__.py +46 -0
  28. kivyforge/config/errors.py +43 -0
  29. kivyforge/config/icons.py +60 -0
  30. kivyforge/config/loader.py +3079 -0
  31. kivyforge/config/model.py +817 -0
  32. kivyforge/doctor/__init__.py +19 -0
  33. kivyforge/doctor/checks_common.py +95 -0
  34. kivyforge/doctor/probe.py +357 -0
  35. kivyforge/doctor/result.py +41 -0
  36. kivyforge/lock/__init__.py +42 -0
  37. kivyforge/lock/_pip_shim.py +63 -0
  38. kivyforge/lock/find_links.py +182 -0
  39. kivyforge/lock/model.py +69 -0
  40. kivyforge/lock/pep751.py +111 -0
  41. kivyforge/lock/reader.py +30 -0
  42. kivyforge/lock/resolver.py +124 -0
  43. kivyforge/lock/wheelruntime/__init__.py +70 -0
  44. kivyforge/lock/wheelruntime/builder.py +280 -0
  45. kivyforge/lock/wheelruntime/model.py +94 -0
  46. kivyforge/lock/wheelruntime/native_binaries.py +127 -0
  47. kivyforge/lock/wheelruntime/pbs_github.py +133 -0
  48. kivyforge/lock/wheelruntime/profile.py +104 -0
  49. kivyforge/lock/wheelruntime/resolver.py +248 -0
  50. kivyforge/lock/wheelruntime/runtime.py +161 -0
  51. kivyforge/lock/wheelruntime/serialize.py +245 -0
  52. kivyforge/platforms/__init__.py +114 -0
  53. kivyforge/platforms/android/__init__.py +140 -0
  54. kivyforge/platforms/android/adb.py +215 -0
  55. kivyforge/platforms/android/bootstrap/__init__.py +7 -0
  56. kivyforge/platforms/android/bootstrap/contract.py +166 -0
  57. kivyforge/platforms/android/bootstrap/render.py +244 -0
  58. kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
  59. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
  60. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
  61. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
  62. kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
  63. kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
  64. kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
  65. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
  66. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
  67. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
  68. kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
  69. kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
  70. kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
  71. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
  72. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
  73. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
  74. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
  75. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
  76. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
  77. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
  78. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
  79. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
  80. kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
  81. kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
  82. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
  83. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
  84. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
  85. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
  86. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
  87. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
  88. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
  89. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
  90. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
  91. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
  92. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
  93. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
  94. kivyforge/platforms/android/cli.py +1023 -0
  95. kivyforge/platforms/android/doctor.py +899 -0
  96. kivyforge/platforms/android/elf.py +148 -0
  97. kivyforge/platforms/android/generate/__init__.py +1 -0
  98. kivyforge/platforms/android/generate/manifest.py +261 -0
  99. kivyforge/platforms/android/generate/project.py +462 -0
  100. kivyforge/platforms/android/generate/services.py +187 -0
  101. kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
  102. kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
  103. kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
  104. kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
  105. kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
  106. kivyforge/platforms/android/gradlew.py +60 -0
  107. kivyforge/platforms/android/icons.py +237 -0
  108. kivyforge/platforms/android/lock/__init__.py +1 -0
  109. kivyforge/platforms/android/lock/archives.py +109 -0
  110. kivyforge/platforms/android/lock/builder.py +387 -0
  111. kivyforge/platforms/android/lock/markers.py +71 -0
  112. kivyforge/platforms/android/lock/maven.py +252 -0
  113. kivyforge/platforms/android/lock/model.py +113 -0
  114. kivyforge/platforms/android/lock/python_meta.py +199 -0
  115. kivyforge/platforms/android/lock/reader.py +206 -0
  116. kivyforge/platforms/android/lock/resolver.py +276 -0
  117. kivyforge/platforms/android/lock/writer.py +121 -0
  118. kivyforge/platforms/android/policy.py +214 -0
  119. kivyforge/platforms/android/signing.py +145 -0
  120. kivyforge/platforms/android/smoke.py +43 -0
  121. kivyforge/platforms/android/splash.py +293 -0
  122. kivyforge/platforms/android/stage/__init__.py +1 -0
  123. kivyforge/platforms/android/stage/bundle.py +295 -0
  124. kivyforge/platforms/android/stage/jnilibs.py +186 -0
  125. kivyforge/platforms/android/stage/runtime.py +61 -0
  126. kivyforge/platforms/android/stage/wheels.py +106 -0
  127. kivyforge/platforms/android/toolchain.py +29 -0
  128. kivyforge/platforms/base.py +157 -0
  129. kivyforge/platforms/ios/__init__.py +129 -0
  130. kivyforge/platforms/ios/assets.py +193 -0
  131. kivyforge/platforms/ios/buildsettings.py +128 -0
  132. kivyforge/platforms/ios/cli.py +543 -0
  133. kivyforge/platforms/ios/doctor.py +410 -0
  134. kivyforge/platforms/ios/entitlements.py +34 -0
  135. kivyforge/platforms/ios/generator.py +318 -0
  136. kivyforge/platforms/ios/lock/__init__.py +71 -0
  137. kivyforge/platforms/ios/lock/builder.py +393 -0
  138. kivyforge/platforms/ios/lock/markers.py +69 -0
  139. kivyforge/platforms/ios/lock/model.py +95 -0
  140. kivyforge/platforms/ios/lock/python_meta.py +187 -0
  141. kivyforge/platforms/ios/lock/reader.py +191 -0
  142. kivyforge/platforms/ios/lock/resolver.py +304 -0
  143. kivyforge/platforms/ios/lock/spm.py +217 -0
  144. kivyforge/platforms/ios/lock/writer.py +114 -0
  145. kivyforge/platforms/ios/lock/xcframework.py +188 -0
  146. kivyforge/platforms/ios/materialize.py +59 -0
  147. kivyforge/platforms/ios/plist.py +91 -0
  148. kivyforge/platforms/ios/privacy.py +39 -0
  149. kivyforge/platforms/ios/skeleton.py +165 -0
  150. kivyforge/platforms/ios/sources.py +63 -0
  151. kivyforge/platforms/ios/staging.py +225 -0
  152. kivyforge/platforms/ios/swift_packages.py +324 -0
  153. kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
  154. kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
  155. kivyforge/platforms/ios/templates/main.m +15 -0
  156. kivyforge/platforms/ios/templates/main_config.h.in +12 -0
  157. kivyforge/platforms/ios/xcframeworks.py +194 -0
  158. kivyforge/platforms/ios/xcode/__init__.py +76 -0
  159. kivyforge/platforms/ios/xcode/commands.py +296 -0
  160. kivyforge/platforms/ios/xcode/runner.py +316 -0
  161. kivyforge/platforms/linux/__init__.py +120 -0
  162. kivyforge/platforms/linux/appimage.py +169 -0
  163. kivyforge/platforms/linux/bundle.py +235 -0
  164. kivyforge/platforms/linux/cli.py +232 -0
  165. kivyforge/platforms/linux/desktop.py +71 -0
  166. kivyforge/platforms/linux/doctor.py +438 -0
  167. kivyforge/platforms/linux/elftools.py +95 -0
  168. kivyforge/platforms/linux/icons.py +129 -0
  169. kivyforge/platforms/linux/launcher.py +107 -0
  170. kivyforge/platforms/linux/lock/__init__.py +126 -0
  171. kivyforge/platforms/linux/lock/profile.py +254 -0
  172. kivyforge/platforms/linux/lock/runtime.py +75 -0
  173. kivyforge/platforms/linux/native_stage.py +47 -0
  174. kivyforge/platforms/linux/runtime_stage.py +107 -0
  175. kivyforge/platforms/linux/wheels_stage.py +103 -0
  176. kivyforge/platforms/macos/__init__.py +121 -0
  177. kivyforge/platforms/macos/bundle.py +255 -0
  178. kivyforge/platforms/macos/cli.py +269 -0
  179. kivyforge/platforms/macos/doctor.py +480 -0
  180. kivyforge/platforms/macos/icns.py +75 -0
  181. kivyforge/platforms/macos/launcher.py +118 -0
  182. kivyforge/platforms/macos/lock/__init__.py +112 -0
  183. kivyforge/platforms/macos/lock/profile.py +119 -0
  184. kivyforge/platforms/macos/lock/runtime.py +58 -0
  185. kivyforge/platforms/macos/machotools.py +138 -0
  186. kivyforge/platforms/macos/native_stage.py +47 -0
  187. kivyforge/platforms/macos/notarize.py +143 -0
  188. kivyforge/platforms/macos/plist.py +37 -0
  189. kivyforge/platforms/macos/runtime_stage.py +110 -0
  190. kivyforge/platforms/macos/signing.py +78 -0
  191. kivyforge/platforms/macos/wheels_stage.py +108 -0
  192. kivyforge/platforms/windows/__init__.py +133 -0
  193. kivyforge/platforms/windows/assets.py +76 -0
  194. kivyforge/platforms/windows/bundle.py +298 -0
  195. kivyforge/platforms/windows/cli.py +314 -0
  196. kivyforge/platforms/windows/doctor.py +563 -0
  197. kivyforge/platforms/windows/fsswap.py +87 -0
  198. kivyforge/platforms/windows/icons.py +64 -0
  199. kivyforge/platforms/windows/launcher/__init__.py +193 -0
  200. kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
  201. kivyforge/platforms/windows/launcher/launcher.c +379 -0
  202. kivyforge/platforms/windows/lock/__init__.py +112 -0
  203. kivyforge/platforms/windows/lock/profile.py +101 -0
  204. kivyforge/platforms/windows/lock/runtime.py +64 -0
  205. kivyforge/platforms/windows/naming.py +52 -0
  206. kivyforge/platforms/windows/native_stage.py +66 -0
  207. kivyforge/platforms/windows/petools.py +98 -0
  208. kivyforge/platforms/windows/rcedit.py +116 -0
  209. kivyforge/platforms/windows/runtime_stage.py +221 -0
  210. kivyforge/platforms/windows/signing.py +168 -0
  211. kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
  212. kivyforge/platforms/windows/vendor/NOTICE +17 -0
  213. kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
  214. kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
  215. kivyforge/platforms/windows/vendor/__init__.py +12 -0
  216. kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
  217. kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
  218. kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
  219. kivyforge/platforms/windows/wheels_stage.py +139 -0
  220. kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
  221. kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
  222. kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
  223. kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
  224. kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  225. kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,314 @@
1
+ """``kivyforge upgrade`` — re-download pinned runtime/native artifacts per the
2
+ resolved platform's lock (spec 05).
3
+
4
+ Platform-aware: resolves the target the same way as ``lock``/``build``/``run``
5
+ (``-p`` / ``KIVYFORGE_PLATFORM`` / host default, gated on the lock already
6
+ being configured). iOS refreshes ``Python.xcframework`` + xcframework
7
+ artifacts; macOS/Linux refresh the pinned python-build-standalone runtime
8
+ archive(s) — their locks have no xcframework-equivalent entries. Dependency
9
+ wheels are deliberately never touched here on any platform (that is
10
+ ``build``'s/``lock``'s job); ``upgrade`` only re-fetches bytes already pinned
11
+ by the existing lock, it never changes what is pinned.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from pathlib import Path
17
+
18
+ import click
19
+
20
+ from ..artifacts.download import DownloadError, fetch_artifact
21
+ from ..artifacts.verify import HashMismatch
22
+ from ..lock import LockError
23
+ from ..platforms.ios.lock import load as load_ios_lock
24
+ from ._common import ToolchainError, lockfile_path_for
25
+ from ._platform import platform_option, resolve_target
26
+
27
+
28
+ @click.command()
29
+ @platform_option
30
+ @click.option(
31
+ "--python",
32
+ "python_only",
33
+ is_flag=True,
34
+ help="Only refresh the bundled Python runtime (iOS: Python.xcframework; "
35
+ "macOS/Linux: the runtime archive(s) — a no-op flag there, it's already "
36
+ "the only refreshable artifact).",
37
+ )
38
+ @click.option(
39
+ "--xcframeworks",
40
+ "xcframeworks_only",
41
+ is_flag=True,
42
+ help="Only refresh native xcframework artifacts (iOS only).",
43
+ )
44
+ @click.option(
45
+ "--libs",
46
+ "libs_only",
47
+ is_flag=True,
48
+ help="Only refresh the locked .aar/.jar artifacts (Android only) — the "
49
+ "Android counterpart of --xcframeworks.",
50
+ )
51
+ @click.option(
52
+ "--name",
53
+ default=None,
54
+ help="Only refresh a specific artifact by name (iOS: an xcframework name "
55
+ "or 'Python.xcframework'; Android: an .aar/.jar name or an ABI; "
56
+ "macOS/Linux: a locked arch, e.g. 'arm64').",
57
+ )
58
+ def upgrade(
59
+ cli_platform: str | None,
60
+ python_only: bool,
61
+ xcframeworks_only: bool,
62
+ libs_only: bool,
63
+ name: str | None,
64
+ ) -> None:
65
+ """Re-fetch pinned runtime/native artifacts for the resolved platform's lock."""
66
+ backend, project_root = resolve_target(cli_platform, verb="upgrade")
67
+
68
+ if libs_only and backend.name != "android":
69
+ raise ToolchainError(
70
+ f"--libs is Android-only; {backend.name} locks have no .aar/.jar "
71
+ + (
72
+ "artifacts. Use --xcframeworks for the iOS equivalent."
73
+ if backend.name == "ios"
74
+ else "artifacts."
75
+ )
76
+ )
77
+ if python_only and libs_only:
78
+ raise ToolchainError(
79
+ "--python and --libs select disjoint halves of the lock; pass one, "
80
+ "or neither to refresh everything."
81
+ )
82
+
83
+ if backend.name == "ios":
84
+ _upgrade_ios(project_root, python_only, xcframeworks_only, name)
85
+ elif backend.name == "android":
86
+ if xcframeworks_only:
87
+ raise ToolchainError(
88
+ "--xcframeworks is iOS-only; Android uses --libs for .aar/.jar."
89
+ )
90
+ _upgrade_android(project_root, python_only, libs_only, name)
91
+ elif backend.name in ("macos", "linux", "windows"):
92
+ if xcframeworks_only:
93
+ raise ToolchainError(
94
+ f"--xcframeworks is iOS-only; {backend.name} locks have no "
95
+ "xcframework artifacts. Use --python (or no flag) to refresh "
96
+ "the bundled runtime."
97
+ )
98
+ _upgrade_wheelruntime(backend.name, project_root, name)
99
+ else:
100
+ raise ToolchainError(
101
+ f"`kivyforge upgrade` does not support platform {backend.name!r} yet."
102
+ )
103
+
104
+
105
+ def _upgrade_android(
106
+ project_root: Path, python_only: bool, libs_only: bool, name: str | None
107
+ ) -> None:
108
+ """Re-fetch the pinned python.org runtime + .aar/.jar per the existing lock.
109
+
110
+ Does not reinstall wheels, regenerate the project, or invoke Gradle
111
+ (android/06 §upgrade): to take newer versions, edit pyproject.toml and
112
+ re-lock.
113
+ """
114
+ from ..platforms.android.lock import reader as android_reader
115
+
116
+ lock_path = project_root / "pylock.android.toml"
117
+ if not lock_path.is_file():
118
+ raise ToolchainError(
119
+ "pylock.android.toml not found. Run `kivyforge lock -p android` first."
120
+ )
121
+ try:
122
+ lock = android_reader.load(lock_path)
123
+ except LockError as exc:
124
+ raise ToolchainError(str(exc)) from exc
125
+
126
+ do_python = not libs_only
127
+ do_libs = not python_only
128
+ refreshed = skipped = 0
129
+ for runtime in lock.python_android if do_python else ():
130
+ if name and name not in (runtime.abi, "python"):
131
+ continue
132
+ if runtime.path:
133
+ skipped += 1
134
+ continue
135
+ click.echo(
136
+ f"Refreshing python.org runtime {runtime.version} ({runtime.abi}) ..."
137
+ )
138
+ fetch_artifact(
139
+ name=f"python-android-{runtime.abi}",
140
+ sha256=runtime.sha256,
141
+ filename=(runtime.url or "").rsplit("/", 1)[-1],
142
+ url=runtime.url,
143
+ project_root=project_root,
144
+ no_cache=True,
145
+ )
146
+ refreshed += 1
147
+ if do_libs:
148
+ for lib in lock.android_libs:
149
+ if name and name != lib.name:
150
+ continue
151
+ if lib.path:
152
+ skipped += 1
153
+ continue
154
+ click.echo(f"Refreshing {lib.kind} {lib.name} {lib.version} ...")
155
+ fetch_artifact(
156
+ name=lib.name,
157
+ sha256=lib.sha256,
158
+ filename=(lib.url or "").rsplit("/", 1)[-1],
159
+ url=lib.url,
160
+ project_root=project_root,
161
+ no_cache=True,
162
+ )
163
+ refreshed += 1
164
+ if name and refreshed == 0 and skipped == 0:
165
+ known = sorted(
166
+ {r.abi for r in lock.python_android}
167
+ | {lib.name for lib in lock.android_libs}
168
+ )
169
+ raise ToolchainError(
170
+ f"no artifact named {name!r} found in {lock_path.name}. "
171
+ f"Known name(s): {', '.join(known) or 'none'} (or 'python' for "
172
+ "every runtime)."
173
+ )
174
+ click.echo(
175
+ f"Refreshed {refreshed} artifact(s)"
176
+ + (f"; {skipped} vendored (path) entry(ies) skipped." if skipped else ".")
177
+ )
178
+
179
+
180
+ # --------------------------------------------------------------------------- #
181
+ # iOS — Python.xcframework + [[xcframeworks]]
182
+ # --------------------------------------------------------------------------- #
183
+ def _upgrade_ios(
184
+ project_root: Path,
185
+ python_only: bool,
186
+ xcframeworks_only: bool,
187
+ name: str | None,
188
+ ) -> None:
189
+ lock = _load_ios_lock(project_root)
190
+
191
+ # No selector flag => refresh everything.
192
+ do_python = python_only or not (python_only or xcframeworks_only or name)
193
+ do_xc = xcframeworks_only or not (python_only or xcframeworks_only or name)
194
+ if name:
195
+ do_python = name == "Python.xcframework"
196
+ do_xc = True
197
+
198
+ refreshed = 0
199
+ skipped_vendored = 0
200
+ try:
201
+ if do_python and (not name or name == "Python.xcframework"):
202
+ px = lock.python_xcframework
203
+ click.echo(f"Refreshing Python.xcframework {px.version} ...")
204
+ fetch_artifact(
205
+ name="Python.xcframework",
206
+ sha256=px.sha256,
207
+ filename=px.url.rsplit("/", 1)[-1],
208
+ url=px.url,
209
+ no_cache=True,
210
+ )
211
+ refreshed += 1
212
+
213
+ if do_xc:
214
+ for xc in lock.xcframeworks:
215
+ if name and xc.name != name:
216
+ continue
217
+ if xc.path:
218
+ skipped_vendored += 1
219
+ continue
220
+ url = xc.url
221
+ if url is None:
222
+ continue
223
+ click.echo(f"Refreshing {xc.name} {xc.version} ...")
224
+ fetch_artifact(
225
+ name=xc.name,
226
+ sha256=xc.sha256,
227
+ filename=url.rsplit("/", 1)[-1],
228
+ url=url,
229
+ project_root=project_root,
230
+ no_cache=True,
231
+ )
232
+ refreshed += 1
233
+ except HashMismatch as exc:
234
+ raise ToolchainError(str(exc)) from exc
235
+ except DownloadError as exc:
236
+ raise ToolchainError(str(exc)) from exc
237
+
238
+ if name and refreshed == 0 and skipped_vendored == 0:
239
+ raise ToolchainError(
240
+ f"no artifact named {name!r} found in {lockfile_path_for('ios').name}."
241
+ )
242
+ msg = f"Refreshed {refreshed} artifact(s)."
243
+ if skipped_vendored:
244
+ msg += f" Skipped {skipped_vendored} vendored (path-based) entry/entries."
245
+ click.echo(msg)
246
+
247
+
248
+ def _load_ios_lock(project_root: Path):
249
+ path = lockfile_path_for("ios", project_root)
250
+ if not path.is_file():
251
+ raise ToolchainError(f"no {path.name} found. Run `kivyforge lock` first.")
252
+ try:
253
+ return load_ios_lock(path)
254
+ except LockError as exc:
255
+ raise ToolchainError(str(exc)) from exc
256
+
257
+
258
+ # --------------------------------------------------------------------------- #
259
+ # macOS / Linux — bundled python-build-standalone runtime archive(s)
260
+ # --------------------------------------------------------------------------- #
261
+ def _upgrade_wheelruntime(
262
+ platform_name: str, project_root: Path, name: str | None
263
+ ) -> None:
264
+ lock = _load_wheelruntime_lock(platform_name, project_root)
265
+ runtime = lock.python_runtime
266
+ artifacts = runtime.artifacts
267
+ if name:
268
+ artifacts = [art for art in artifacts if art.arch == name]
269
+
270
+ refreshed = 0
271
+ try:
272
+ for art in artifacts:
273
+ click.echo(
274
+ f"Refreshing the {platform_name} Python runtime "
275
+ f"({art.arch}) {runtime.version} ..."
276
+ )
277
+ fetch_artifact(
278
+ name=f"python-runtime-{art.arch}",
279
+ sha256=art.sha256,
280
+ filename=art.url.rsplit("/", 1)[-1],
281
+ url=art.url,
282
+ project_root=project_root,
283
+ no_cache=True,
284
+ )
285
+ refreshed += 1
286
+ except HashMismatch as exc:
287
+ raise ToolchainError(str(exc)) from exc
288
+ except DownloadError as exc:
289
+ raise ToolchainError(str(exc)) from exc
290
+
291
+ if name and refreshed == 0:
292
+ locked = ", ".join(sorted(art.arch for art in runtime.artifacts))
293
+ path = lockfile_path_for(platform_name, project_root)
294
+ raise ToolchainError(
295
+ f"no runtime artifact for arch {name!r} in {path.name}. "
296
+ f"Locked arch(s): {locked}."
297
+ )
298
+ click.echo(f"Refreshed {refreshed} artifact(s).")
299
+
300
+
301
+ def _load_wheelruntime_lock(platform_name: str, project_root: Path):
302
+ path = lockfile_path_for(platform_name, project_root)
303
+ if not path.is_file():
304
+ raise ToolchainError(f"no {path.name} found. Run `kivyforge lock` first.")
305
+ if platform_name == "macos":
306
+ from ..platforms.macos.lock import load as loader
307
+ elif platform_name == "windows":
308
+ from ..platforms.windows.lock import load as loader
309
+ else:
310
+ from ..platforms.linux.lock import load as loader
311
+ try:
312
+ return loader(path)
313
+ except LockError as exc:
314
+ raise ToolchainError(str(exc)) from exc
@@ -0,0 +1,46 @@
1
+ """kivyforge project configuration (pyproject.toml) model and loader (spec 01)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .errors import ConfigError
6
+ from .loader import load_config, load_config_from_text
7
+ from .model import (
8
+ Author,
9
+ Config,
10
+ DesktopConfig,
11
+ IconConfig,
12
+ IosConfig,
13
+ KivyMeta,
14
+ LinuxConfig,
15
+ MacosConfig,
16
+ NativeBinaryDep,
17
+ ProjectMeta,
18
+ SigningConfig,
19
+ SplashConfig,
20
+ SwiftPackageDep,
21
+ WindowsConfig,
22
+ WindowsSigningConfig,
23
+ XcframeworkDep,
24
+ )
25
+
26
+ __all__ = [
27
+ "ConfigError",
28
+ "load_config",
29
+ "load_config_from_text",
30
+ "Author",
31
+ "Config",
32
+ "DesktopConfig",
33
+ "IconConfig",
34
+ "IosConfig",
35
+ "KivyMeta",
36
+ "LinuxConfig",
37
+ "MacosConfig",
38
+ "NativeBinaryDep",
39
+ "ProjectMeta",
40
+ "SigningConfig",
41
+ "SplashConfig",
42
+ "SwiftPackageDep",
43
+ "WindowsConfig",
44
+ "WindowsSigningConfig",
45
+ "XcframeworkDep",
46
+ ]
@@ -0,0 +1,43 @@
1
+ """Configuration validation errors.
2
+
3
+ Kept free of any CLI/click dependency so the config layer is importable and
4
+ testable in isolation. The CLI converts ``ConfigError`` into a clean
5
+ user-facing message.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+
11
+ class ConfigError(Exception):
12
+ """A semantic or structural problem in ``pyproject.toml``.
13
+
14
+ Carries an optional ``key_path`` (dotted TOML location, e.g.
15
+ ``tool.kivy.ios.bundle_id``) and a best-effort ``line`` number so the CLI
16
+ can point the user at the offending spot with a remediation ``hint``.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ message: str,
22
+ *,
23
+ key_path: str | None = None,
24
+ line: int | None = None,
25
+ hint: str | None = None,
26
+ ) -> None:
27
+ self.message = message
28
+ self.key_path = key_path
29
+ self.line = line
30
+ self.hint = hint
31
+ super().__init__(message)
32
+
33
+ def format(self) -> str:
34
+ parts = []
35
+ location = ""
36
+ if self.line is not None:
37
+ location = f" (line {self.line})"
38
+ elif self.key_path is not None:
39
+ location = f" ([{self.key_path}])"
40
+ parts.append(f"{self.message}{location}")
41
+ if self.hint:
42
+ parts.append(f" hint: {self.hint}")
43
+ return "\n".join(parts)
@@ -0,0 +1,60 @@
1
+ """Validate a configured ``[tool.kivy.<platform>.icons].source`` (spec 01).
2
+
3
+ Shared across the iOS, macOS, and Linux backends: the icon source must be an
4
+ existing 1024x1024 PNG. This module only validates the source file; each
5
+ platform decides what to do with it (iOS uses the single-size ``AppIcon``
6
+ catalog entry as-is, macOS/Linux resize into their icon sets).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import struct
12
+ from pathlib import Path
13
+
14
+ APP_ICON_SIZE = 1024
15
+ _PNG_SIG = b"\x89PNG\r\n\x1a\n"
16
+
17
+
18
+ class IconSourceError(Exception):
19
+ """A configured ``[tool.kivy.<platform>.icons].source`` failed validation."""
20
+
21
+
22
+ def png_dimensions(path: Path) -> tuple[int, int]:
23
+ """Read width and height from a PNG IHDR chunk."""
24
+ with path.open("rb") as fh:
25
+ header = fh.read(24)
26
+ if len(header) < 24 or header[:8] != _PNG_SIG:
27
+ raise IconSourceError(f"{path}: not a PNG file (expected 1024x1024 PNG)")
28
+ return struct.unpack(">II", header[16:24])
29
+
30
+
31
+ def validate_icon_source(path: Path) -> None:
32
+ """Fail fast when the icon path is missing, not PNG, or not 1024x1024."""
33
+ if not path.is_file():
34
+ raise IconSourceError(
35
+ f"app icon not found: {path}\n"
36
+ " [tool.kivy.<platform>.icons].source must point to an existing "
37
+ f"{APP_ICON_SIZE}x{APP_ICON_SIZE} PNG."
38
+ )
39
+ try:
40
+ width, height = png_dimensions(path)
41
+ except IconSourceError:
42
+ raise
43
+ except OSError as exc:
44
+ raise IconSourceError(f"cannot read app icon {path}: {exc}") from exc
45
+ if (width, height) != (APP_ICON_SIZE, APP_ICON_SIZE):
46
+ raise IconSourceError(
47
+ f"app icon {path} is {width}x{height}; "
48
+ f"expected {APP_ICON_SIZE}x{APP_ICON_SIZE} PNG.\n"
49
+ f" Resize the source image to {APP_ICON_SIZE}x{APP_ICON_SIZE} "
50
+ "before building."
51
+ )
52
+
53
+
54
+ def icon_source_problem(path: Path) -> str | None:
55
+ """Return an error message, or None when the icon source is valid."""
56
+ try:
57
+ validate_icon_source(path)
58
+ except IconSourceError as exc:
59
+ return str(exc)
60
+ return None