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,817 @@
1
+ """Typed model of the kivyforge ``pyproject.toml`` surface (spec 01).
2
+
3
+ These dataclasses are the validated, in-memory representation produced by
4
+ ``kivyforge.config.loader.load_config``. They intentionally only model the
5
+ fields kivyforge consumes; unknown keys elsewhere in ``pyproject.toml`` are
6
+ ignored (PEP 518 tool-namespace convention).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+
13
+ # The iOS schema major version this build understands (spec 01).
14
+ SUPPORTED_IOS_SCHEMA_VERSION = 1
15
+
16
+ # The macOS overlay schema major version this build understands (macos-spec).
17
+ SUPPORTED_MACOS_SCHEMA_VERSION = 1
18
+
19
+ # The Linux overlay schema major version this build understands (linux-spec).
20
+ SUPPORTED_LINUX_SCHEMA_VERSION = 1
21
+
22
+ # macOS build architecture. arm64 only: macOS 27 stops installing on Intel
23
+ # hardware and macOS 28 removes Rosetta, so an x86_64 slice would be one nothing
24
+ # can execute or validate. Kept list-shaped for symmetry with the other
25
+ # platforms, but there is no second arch to add and no universal2 build.
26
+ #
27
+ # This is the *build target* set, deliberately distinct from the *wheel tag* set
28
+ # (macos/lock/profile.py VALID_WHEEL_ARCHS): a universal2 wheel is a perfectly
29
+ # good arm64 wheel and is still accepted.
30
+ VALID_MACOS_ARCHS = frozenset({"arm64"})
31
+ DEFAULT_MACOS_ARCHS = ("arm64",)
32
+
33
+ # Linux build architectures. Only x86_64 is allowed this phase; the field stays
34
+ # list-shaped so aarch64 is purely additive later (linux-spec). Unlike macOS
35
+ # there is no fat binary — each arch would be a separate AppImage.
36
+ VALID_LINUX_ARCHS = frozenset({"x86_64"})
37
+ DEFAULT_LINUX_ARCHS = ("x86_64",)
38
+
39
+ # The Windows overlay schema major version this build understands (windows-spec).
40
+ SUPPORTED_WINDOWS_SCHEMA_VERSION = 1
41
+
42
+ # Windows build architectures. Only amd64 (x86-64) this phase; the field stays
43
+ # list-shaped so win-arm64 is purely additive later (windows-spec). Like Linux
44
+ # and unlike macOS there is no fat binary — each arch is a separate onedir.
45
+ # arm64: add "arm64" to VALID_WINDOWS_ARCHS to accept it; DEFAULT stays amd64
46
+ # (arm64 is opt-in via [tool.kivy.windows].archs). See arm64-windows.md §1.
47
+ VALID_WINDOWS_ARCHS = frozenset({"amd64"})
48
+ DEFAULT_WINDOWS_ARCHS = ("amd64",)
49
+
50
+ # Microsoft's real AppUserModelID hard constraints (windows-spec): no spaces and
51
+ # at most this many characters. Pascal-case / period-delimited *style* is only a
52
+ # doctor warning, so the loader enforces just these two.
53
+ WINDOWS_APP_ID_MAX_LENGTH = 128
54
+
55
+ # The Android overlay schema major version this build understands (spec
56
+ # platforms/android/01).
57
+ SUPPORTED_ANDROID_SCHEMA_VERSION = 1
58
+
59
+ # Android ABIs. 64-bit only, permanently for this schema: the python.org
60
+ # Android runtime ships no 32-bit build (android/01 §ABIs). Underscored
61
+ # wheel-tag spelling; the generator maps to Android's dashed names.
62
+ VALID_ANDROID_ABIS = frozenset({"arm64_v8a", "x86_64"})
63
+ DEFAULT_ANDROID_ABIS = ("arm64_v8a", "x86_64")
64
+
65
+ # minSdk floor: first API level with RUNPATH (auditwheel's requirement), the
66
+ # python.org runtime floor, and the android_* wheel-tag ecosystem floor.
67
+ ANDROID_MIN_SDK_FLOOR = 24
68
+ DEFAULT_ANDROID_MIN_SDK = 24
69
+ # targetSdk default: the latest stable API level this kivyforge release knows.
70
+ DEFAULT_ANDROID_TARGET_SDK = 35
71
+
72
+ # Kivy generations the bootstrap supports: 2 (Kivy 2.3.1, SDL2) or 3 (Kivy
73
+ # 3.0, SDL3). Named after the Kivy major version, not the SDL generation it
74
+ # happens to pair with, so configuring a build never requires knowing that
75
+ # pairing.
76
+ VALID_ANDROID_KIVY_GENERATIONS = frozenset({2, 3})
77
+ DEFAULT_ANDROID_KIVY_GENERATION = 2
78
+
79
+ # Google Play's versionCode ceiling; the auto-derivation formula must stay
80
+ # under it (android/01 §auto-derived version_code).
81
+ ANDROID_VERSION_CODE_CEILING = 2_100_000_000
82
+
83
+ # android:foregroundServiceType values (android/01). Tracks the platform list
84
+ # per kivyforge release; mediaProcessing requires Android 15 / API 35.
85
+ VALID_FOREGROUND_SERVICE_TYPES = frozenset(
86
+ {
87
+ "camera",
88
+ "connectedDevice",
89
+ "dataSync",
90
+ "health",
91
+ "location",
92
+ "mediaPlayback",
93
+ "mediaProcessing",
94
+ "mediaProjection",
95
+ "microphone",
96
+ "phoneCall",
97
+ "remoteMessaging",
98
+ "shortService",
99
+ "specialUse",
100
+ "systemExempted",
101
+ }
102
+ )
103
+
104
+ # byte_compile / strip_source / strip_native_libs accept a bool or this string.
105
+ ANDROID_RELEASE_ONLY = "release"
106
+ VALID_ANDROID_DEBUG_SYMBOLS = frozenset({"symbol_table", "full", "none"})
107
+
108
+ # Default env-var names for the release keystore passwords (android/01).
109
+ DEFAULT_ANDROID_STORE_PASSWORD_ENV = "KIVYFORGE_KEYSTORE_PASSWORD"
110
+ DEFAULT_ANDROID_KEY_PASSWORD_ENV = "KIVYFORGE_KEY_PASSWORD"
111
+
112
+ # Default app theme parent (android/01 base_theme).
113
+ DEFAULT_ANDROID_BASE_THEME = "Theme.Material3.DayNight.NoActionBar"
114
+
115
+ # Manifest attributes kivyforge manages (android/01 §managed keys): rejected in
116
+ # the [tool.kivy.android.manifest] application/activity passthrough tables.
117
+ MANAGED_ANDROID_APPLICATION_ATTRS = frozenset(
118
+ {
119
+ "android:label",
120
+ "android:icon",
121
+ "android:roundIcon",
122
+ "android:theme",
123
+ "android:extractNativeLibs",
124
+ }
125
+ )
126
+ MANAGED_ANDROID_ACTIVITY_ATTRS = frozenset(
127
+ {
128
+ "android:name",
129
+ "android:label",
130
+ "android:screenOrientation",
131
+ "android:theme",
132
+ }
133
+ )
134
+
135
+ # gradle.properties keys kivyforge manages (android/01 gradle_properties).
136
+ RESERVED_ANDROID_GRADLE_PROPERTIES = frozenset({"android.useAndroidX"})
137
+
138
+ # Intent-filter <data> attribute names accepted by the structured table.
139
+ VALID_INTENT_DATA_KEYS = frozenset(
140
+ {"scheme", "host", "port", "path", "pathPrefix", "pathPattern", "mimeType"}
141
+ )
142
+
143
+ # Default Authenticode RFC-3161 timestamp server (signing-windows).
144
+ DEFAULT_WINDOWS_TIMESTAMP_URL = "http://timestamp.digicert.com"
145
+
146
+ # Certificate-store scopes for signtool / the doctor certificate check
147
+ # (signing-windows). ``current_user`` -> ``Cert:\CurrentUser\My``; ``machine``
148
+ # -> ``Cert:\LocalMachine\My`` and adds signtool's ``/sm``.
149
+ VALID_WINDOWS_STORE_SCOPES = frozenset({"current_user", "machine"})
150
+ DEFAULT_WINDOWS_STORE_SCOPE = "current_user"
151
+
152
+ # Default freedesktop main category for the generated .desktop entry.
153
+ DEFAULT_DESKTOP_CATEGORIES = ("Utility",)
154
+
155
+ # The freedesktop **main** categories (desktop-entry menu spec). A generated
156
+ # ``.desktop`` must carry categories from this set so it registers correctly in
157
+ # menus/search; a typo/invalid value is rejected at config time (linux-spec).
158
+ FREEDESKTOP_MAIN_CATEGORIES = frozenset(
159
+ {
160
+ "AudioVideo",
161
+ "Audio",
162
+ "Video",
163
+ "Development",
164
+ "Education",
165
+ "Game",
166
+ "Graphics",
167
+ "Network",
168
+ "Office",
169
+ "Science",
170
+ "Settings",
171
+ "System",
172
+ "Utility",
173
+ }
174
+ )
175
+
176
+ VALID_ORIENTATIONS = frozenset(
177
+ {"portrait", "portrait-upside-down", "landscape-left", "landscape-right"}
178
+ )
179
+
180
+ DEFAULT_DEPLOYMENT_TARGET = "13.0"
181
+ DEFAULT_ENTRY_POINT = "main"
182
+ DEFAULT_ORIENTATION = ("portrait",)
183
+
184
+ # Simulator architectures pinned by ``kivyforge lock`` (spec 01/02). The device
185
+ # slice is always arm64; these are the *simulator* slices. arm64 only: the
186
+ # x86_64 simulator slice had value only on an Intel Mac host, and macOS 27 stops
187
+ # installing on Intel hardware.
188
+ VALID_SIMULATOR_ARCHS = frozenset({"arm64"})
189
+ DEFAULT_SIMULATOR_ARCHS = ("arm64",)
190
+
191
+ # Info.plist keys kivyforge writes from the schema; users may not set these via
192
+ # [tool.kivy.ios.info_plist] (spec 01).
193
+ MANAGED_INFO_PLIST_KEYS = frozenset(
194
+ {
195
+ "CFBundleName",
196
+ "CFBundleDisplayName",
197
+ "CFBundleIdentifier",
198
+ "CFBundleShortVersionString",
199
+ "CFBundleVersion",
200
+ "MinimumOSVersion",
201
+ "UISupportedInterfaceOrientations",
202
+ "UISupportedInterfaceOrientations~ipad",
203
+ "LSRequiresIPhoneOS",
204
+ "CFBundlePackageType",
205
+ "CFBundleInfoDictionaryVersion",
206
+ "CFBundleExecutable",
207
+ "NSHumanReadableCopyright",
208
+ # SDL3 scene lifecycle — written automatically for all Kivy apps.
209
+ "UIApplicationSceneManifest",
210
+ }
211
+ )
212
+
213
+ # Xcode build settings the toolchain manages; rejected under
214
+ # [tool.kivy.ios.xcode.build_settings] (spec 01).
215
+ RESERVED_BUILD_SETTINGS = frozenset(
216
+ {
217
+ "INFOPLIST_FILE",
218
+ "PRODUCT_BUNDLE_IDENTIFIER",
219
+ "IPHONEOS_DEPLOYMENT_TARGET",
220
+ "TARGETED_DEVICE_FAMILY",
221
+ "DEBUG_INFORMATION_FORMAT",
222
+ "CODE_SIGN_STYLE",
223
+ "CODE_SIGN_IDENTITY",
224
+ "DEVELOPMENT_TEAM",
225
+ "PROVISIONING_PROFILE_SPECIFIER",
226
+ "ENABLE_USER_SCRIPT_SANDBOXING",
227
+ "ENABLE_TESTABILITY",
228
+ "FRAMEWORK_SEARCH_PATHS",
229
+ "HEADER_SEARCH_PATHS",
230
+ "LD_RUNPATH_SEARCH_PATHS",
231
+ "GCC_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER",
232
+ }
233
+ )
234
+
235
+
236
+ @dataclass(frozen=True)
237
+ class Author:
238
+ name: str | None = None
239
+ email: str | None = None
240
+
241
+
242
+ @dataclass(frozen=True)
243
+ class ProjectMeta:
244
+ """PEP 621 ``[project]`` subset consumed by kivyforge."""
245
+
246
+ name: str
247
+ version: str
248
+ description: str | None = None
249
+ requires_python: str | None = None
250
+ dependencies: tuple[str, ...] = ()
251
+ authors: tuple[Author, ...] = ()
252
+
253
+
254
+ @dataclass(frozen=True)
255
+ class IconConfig:
256
+ source: str | None = None
257
+
258
+
259
+ @dataclass(frozen=True)
260
+ class SplashConfig:
261
+ source: str | None = None
262
+ background: str | None = None
263
+
264
+
265
+ @dataclass(frozen=True)
266
+ class XcframeworkDep:
267
+ name: str
268
+ version: str
269
+ source: str
270
+ link: bool = True
271
+ embed: bool = True
272
+
273
+
274
+ @dataclass(frozen=True)
275
+ class NativeBinaryDep:
276
+ """One ``[tool.kivy.<platform>.native.binaries]`` entry (macos-spec).
277
+
278
+ A non-wheel native binary (a vendor SDK DLL/dylib/so, or a helper
279
+ executable) declared by name. ``source`` is always explicit: a direct
280
+ ``http(s)://`` URL, or a repo-relative path to a vendored artifact. The
281
+ entry is SHA-256-pinned into the lock and staged into the bundle's ``bin``
282
+ directory at build time (see ``NativeBinaryDep`` handling in each desktop
283
+ backend).
284
+ """
285
+
286
+ name: str
287
+ version: str
288
+ source: str
289
+
290
+
291
+ # byte_compile / strip_source (desktop build_settings) accept a bool or this
292
+ # string, applying only to `kivyforge package` builds -- shared across
293
+ # Windows/Linux/macOS/iOS since the setting and its semantics are identical on
294
+ # all four. Distinct from ANDROID_RELEASE_ONLY (same value, kept separate so
295
+ # nothing here can accidentally couple to Android's own tri-state).
296
+ DESKTOP_RELEASE_ONLY = "release"
297
+
298
+
299
+ @dataclass(frozen=True)
300
+ class DesktopBuildSettings:
301
+ """``[tool.kivy.<platform>.build_settings]`` -- shared across Windows,
302
+ Linux, macOS (macos-spec / windows-spec / linux-spec), and iOS's
303
+ ``[tool.kivy.ios.python.build_settings]`` (pyproject-ios).
304
+
305
+ Deliberately smaller than ``AndroidBuildSettings``: these backends have no
306
+ Gradle/R8, so there is no ``minify``/``shrink_resources``/``multidex``
307
+ analog, and no native-library stripping distinct from ``strip_source``.
308
+
309
+ ``byte_compile`` / ``strip_source`` are a bool or the string ``"release"``
310
+ (apply to `kivyforge package` only -- the default; a plain `build` always
311
+ keeps source, matching the dev-loop/shippable-artifact split each backend
312
+ already draws for signing). **stdlib is never stripped** regardless
313
+ of this setting -- only the app's own code and installed wheels are, since
314
+ the platform runtime ships its stdlib pre-compiled already.
315
+ """
316
+
317
+ byte_compile: bool | str = DESKTOP_RELEASE_ONLY
318
+ strip_source: bool | str = DESKTOP_RELEASE_ONLY
319
+
320
+
321
+ # SPM version-rule kinds (spec 07), mapping 1:1 to Swift Package Manager's own
322
+ # requirement rules. Each ``requirement`` inline table sets exactly one of these.
323
+ VALID_SWIFT_REQUIREMENT_KINDS = frozenset(
324
+ {"exact", "from", "up_to_next_minor", "range", "branch", "revision"}
325
+ )
326
+
327
+
328
+ @dataclass(frozen=True)
329
+ class SwiftPackageDep:
330
+ """One ``[tool.kivy.ios.native.swift_packages]`` entry (spec 07).
331
+
332
+ Exactly one of ``url`` (remote, with a ``requirement`` rule) or ``path``
333
+ (local, repo-relative) is set. ``requirement`` is a single-key inline table
334
+ (one of ``VALID_SWIFT_REQUIREMENT_KINDS``); it is ``None`` for local
335
+ packages.
336
+ """
337
+
338
+ name: str
339
+ products: tuple[str, ...]
340
+ url: str | None = None
341
+ path: str | None = None
342
+ requirement: dict[str, object] | None = None
343
+ link: bool = True
344
+ embed: bool = True
345
+
346
+ def __post_init__(self) -> None:
347
+ if bool(self.url) == bool(self.path):
348
+ raise ValueError(
349
+ f"swift package {self.name!r} must have exactly one of url/path"
350
+ )
351
+
352
+
353
+ @dataclass(frozen=True)
354
+ class SigningConfig:
355
+ team_id: str = ""
356
+ identity: str = "Apple Development"
357
+ provisioning_profile: str = ""
358
+ auto_signing: bool = True
359
+ upload_symbols: bool = True
360
+
361
+
362
+ @dataclass(frozen=True)
363
+ class IosConfig:
364
+ """``[tool.kivy.ios]`` overlay."""
365
+
366
+ schema_version: int
367
+ bundle_id: str
368
+ build: int = 1
369
+ deployment_target: str = DEFAULT_DEPLOYMENT_TARGET
370
+ simulator_archs: tuple[str, ...] = DEFAULT_SIMULATOR_ARCHS
371
+ extra_index_urls: tuple[str, ...] = ()
372
+ find_links: tuple[str, ...] = ()
373
+ exclude: tuple[str, ...] = ()
374
+ python_version: str | None = None
375
+ icons: IconConfig = field(default_factory=IconConfig)
376
+ splash: SplashConfig = field(default_factory=SplashConfig)
377
+ xcframeworks: tuple[XcframeworkDep, ...] = ()
378
+ swift_packages: tuple[SwiftPackageDep, ...] = ()
379
+ entitlements: dict[str, object] = field(default_factory=dict)
380
+ signing: SigningConfig = field(default_factory=SigningConfig)
381
+ privacy_manifest_source: str | None = None
382
+ info_plist: dict[str, object] = field(default_factory=dict)
383
+ build_settings: dict[str, str] = field(default_factory=dict)
384
+ python_build_settings: DesktopBuildSettings = field(
385
+ default_factory=DesktopBuildSettings
386
+ )
387
+
388
+
389
+ @dataclass(frozen=True)
390
+ class MacosSigningConfig:
391
+ """``[tool.kivy.macos.signing]`` (macos-spec, Developer ID workstream).
392
+
393
+ ``identity`` empty means Developer ID signing is not configured and
394
+ ``package`` falls back to the ad-hoc floor. ``notary_profile`` names a
395
+ keychain profile created with ``xcrun notarytool store-credentials`` —
396
+ credentials stay in the keychain, never in ``pyproject.toml``.
397
+ """
398
+
399
+ identity: str = ""
400
+ team_id: str = ""
401
+ notary_profile: str = ""
402
+
403
+ @property
404
+ def configured(self) -> bool:
405
+ return bool(self.identity)
406
+
407
+
408
+ @dataclass(frozen=True)
409
+ class MacosConfig:
410
+ """``[tool.kivy.macos]`` overlay (macos-spec).
411
+
412
+ ``archs`` is a lock-level property: it drives which macOS wheel tags and
413
+ per-arch runtimes the lock must cover. ``minimum_system_version`` is
414
+ ``None`` when unset, meaning the bundled runtime's own floor applies.
415
+ """
416
+
417
+ schema_version: int
418
+ bundle_id: str
419
+ build: int = 1
420
+ minimum_system_version: str | None = None
421
+ archs: tuple[str, ...] = DEFAULT_MACOS_ARCHS
422
+ extra_index_urls: tuple[str, ...] = ()
423
+ find_links: tuple[str, ...] = ()
424
+ exclude: tuple[str, ...] = ()
425
+ python_version: str | None = None
426
+ icons: IconConfig = field(default_factory=IconConfig)
427
+ entitlements: dict[str, object] = field(default_factory=dict)
428
+ signing: MacosSigningConfig = field(default_factory=MacosSigningConfig)
429
+ binaries: tuple[NativeBinaryDep, ...] = ()
430
+ build_settings: DesktopBuildSettings = field(default_factory=DesktopBuildSettings)
431
+
432
+
433
+ @dataclass(frozen=True)
434
+ class WindowsSigningConfig:
435
+ """``[tool.kivy.windows.signing]`` (signing-windows, optional).
436
+
437
+ Authenticode identity is a certificate-store **thumbprint**, never a
438
+ ``.pfx`` path/password. ``store_scope`` selects which store both ``signtool``
439
+ (via ``/sm`` for ``machine``) and the doctor certificate check inspect, so
440
+ they always agree. ``thumbprint`` empty means signing is not configured and
441
+ ``package`` leaves the artifact unsigned.
442
+ """
443
+
444
+ thumbprint: str = ""
445
+ timestamp_url: str = DEFAULT_WINDOWS_TIMESTAMP_URL
446
+ store_scope: str = DEFAULT_WINDOWS_STORE_SCOPE
447
+
448
+ @property
449
+ def configured(self) -> bool:
450
+ return bool(self.thumbprint)
451
+
452
+
453
+ @dataclass(frozen=True)
454
+ class WindowsConfig:
455
+ """``[tool.kivy.windows]`` overlay (windows-spec).
456
+
457
+ ``archs`` drives which ``win_amd64`` wheel tag and per-arch runtime the lock
458
+ must cover. ``app_id`` is the Windows **AppUserModelID** — a different
459
+ identifier from the Linux reverse-DNS ``app_id`` and the macOS ``bundle_id``;
460
+ the loader enforces only Microsoft's hard constraints (no spaces, ≤128
461
+ chars), leaving pascal-case/period style to a doctor warning.
462
+ """
463
+
464
+ schema_version: int
465
+ app_id: str
466
+ archs: tuple[str, ...] = DEFAULT_WINDOWS_ARCHS
467
+ extra_index_urls: tuple[str, ...] = ()
468
+ find_links: tuple[str, ...] = ()
469
+ exclude: tuple[str, ...] = ()
470
+ python_version: str | None = None
471
+ icons: IconConfig = field(default_factory=IconConfig)
472
+ signing: WindowsSigningConfig = field(default_factory=WindowsSigningConfig)
473
+ binaries: tuple[NativeBinaryDep, ...] = ()
474
+ build_settings: DesktopBuildSettings = field(default_factory=DesktopBuildSettings)
475
+
476
+
477
+ @dataclass(frozen=True)
478
+ class AndroidPythonConfig:
479
+ """``[tool.kivy.android.python]`` — the python.org Android runtime pin."""
480
+
481
+ version: str
482
+
483
+
484
+ @dataclass(frozen=True)
485
+ class AndroidFeature:
486
+ """One ``[tool.kivy.android.permissions].features`` entry."""
487
+
488
+ name: str
489
+ required: bool = True
490
+
491
+
492
+ @dataclass(frozen=True)
493
+ class AndroidPermissions:
494
+ """``[tool.kivy.android.permissions]`` (android/01)."""
495
+
496
+ uses: tuple[str, ...] = ()
497
+ features: tuple[AndroidFeature, ...] = ()
498
+ auto_features: bool = True
499
+
500
+
501
+ @dataclass(frozen=True)
502
+ class AndroidIconConfig:
503
+ """``[tool.kivy.android.icons]`` — adaptive-icon inputs."""
504
+
505
+ source: str | None = None
506
+ background: str | None = None
507
+ monochrome: str | None = None
508
+
509
+
510
+ @dataclass(frozen=True)
511
+ class AndroidSplashConfig:
512
+ """``[tool.kivy.android.splash]`` — AndroidX SplashScreen inputs."""
513
+
514
+ source: str | None = None
515
+ background: str | None = None
516
+ icon_background: str | None = None
517
+ animation_duration: int | None = None
518
+ branding: str | None = None
519
+
520
+
521
+ @dataclass(frozen=True)
522
+ class AndroidArchiveDep:
523
+ """One ``[tool.kivy.android.native.aars]`` / ``.jars`` entry (channel 3)."""
524
+
525
+ name: str
526
+ version: str
527
+ source: str
528
+ kind: str # "aar" | "jar"
529
+
530
+
531
+ @dataclass(frozen=True)
532
+ class AndroidGradleConfig:
533
+ """``[tool.kivy.android.gradle]`` — Maven coordinates (channel 4)."""
534
+
535
+ dependencies: tuple[str, ...] = ()
536
+ repositories: tuple[str, ...] = ()
537
+
538
+
539
+ @dataclass(frozen=True)
540
+ class AndroidIncludeFile:
541
+ """One ``[[tool.kivy.android.include_files]]`` entry."""
542
+
543
+ dest: str
544
+ sources: tuple[str, ...]
545
+
546
+
547
+ @dataclass(frozen=True)
548
+ class AndroidSrcConfig:
549
+ """``[tool.kivy.android.src]`` — extra Java/Kotlin source roots."""
550
+
551
+ java: tuple[str, ...] = ()
552
+ kotlin: tuple[str, ...] = ()
553
+
554
+
555
+ @dataclass(frozen=True)
556
+ class AndroidNotification:
557
+ """The mandatory foreground-service notification (android/01 services)."""
558
+
559
+ channel_id: str
560
+ channel_name: str
561
+ title: str
562
+ text: str
563
+ icon: str | None = None
564
+
565
+
566
+ @dataclass(frozen=True)
567
+ class AndroidService:
568
+ """One ``[[tool.kivy.android.services]]`` entry."""
569
+
570
+ name: str
571
+ entry_point: str
572
+ exported: bool = False
573
+ foreground: bool = False
574
+ foreground_service_type: str | None = None
575
+ notification: AndroidNotification | None = None
576
+
577
+
578
+ @dataclass(frozen=True)
579
+ class AndroidActivity:
580
+ """One ``[[tool.kivy.android.activities]]`` entry."""
581
+
582
+ name: str
583
+ exported: bool = False
584
+
585
+
586
+ @dataclass(frozen=True)
587
+ class AndroidIntentFilter:
588
+ """One ``[[tool.kivy.android.intent_filters]]`` entry (main activity)."""
589
+
590
+ action: str
591
+ categories: tuple[str, ...] = ()
592
+ data: tuple[dict[str, str], ...] = ()
593
+
594
+
595
+ @dataclass(frozen=True)
596
+ class AndroidManifestConfig:
597
+ """``[tool.kivy.android.manifest]`` — structured + raw-XML passthrough."""
598
+
599
+ application: dict[str, object] = field(default_factory=dict)
600
+ activity: dict[str, object] = field(default_factory=dict)
601
+ placeholders: dict[str, str] = field(default_factory=dict)
602
+ # Component names the release policy tolerates as exported, on top of the
603
+ # bootstrap's own. Needed because the policy lints the *merged* manifest, so
604
+ # a library's exported component is otherwise unfixable from pyproject.toml.
605
+ allow_exported: tuple[str, ...] = ()
606
+ extra_manifest_xml: str = ""
607
+ extra_application_xml: str = ""
608
+ extra_activity_xml: str = ""
609
+
610
+
611
+ @dataclass(frozen=True)
612
+ class AndroidSigningConfig:
613
+ """``[tool.kivy.android.signing]`` (android/01, android/07).
614
+
615
+ Passwords are **never** stored in ``pyproject.toml`` — only the names of the
616
+ environment variables that hold them. ``keystore`` empty means release
617
+ signing is not configured; ``kivyforge package`` then fails its preflight.
618
+ ``v1_signing`` defaults off: the ``min_sdk`` floor is 24, where the platform
619
+ verifies v2+.
620
+ """
621
+
622
+ keystore: str = ""
623
+ key_alias: str = ""
624
+ store_password_env: str = DEFAULT_ANDROID_STORE_PASSWORD_ENV
625
+ key_password_env: str = DEFAULT_ANDROID_KEY_PASSWORD_ENV
626
+ v1_signing: bool = False
627
+ v2_signing: bool = True
628
+ v3_signing: bool = True
629
+ v4_signing: bool = False
630
+
631
+ @property
632
+ def configured(self) -> bool:
633
+ return bool(self.keystore and self.key_alias)
634
+
635
+
636
+ @dataclass(frozen=True)
637
+ class AndroidBuildSettings:
638
+ """``[tool.kivy.android.build_settings]`` (android/01).
639
+
640
+ ``byte_compile`` / ``strip_source`` / ``strip_native_libs`` are a bool or
641
+ the string ``"release"`` (apply to release packaging only — the default).
642
+ """
643
+
644
+ minify: bool = False
645
+ shrink_resources: bool = False
646
+ multidex: bool = True
647
+ byte_compile: bool | str = ANDROID_RELEASE_ONLY
648
+ strip_source: bool | str = ANDROID_RELEASE_ONLY
649
+ strip_native_libs: bool | str = ANDROID_RELEASE_ONLY
650
+ debug_symbols: str = "symbol_table"
651
+
652
+
653
+ @dataclass(frozen=True)
654
+ class AndroidConfig:
655
+ """``[tool.kivy.android]`` overlay (android/01).
656
+
657
+ ``version_code`` is the **resolved** integer: an explicit value verbatim, or
658
+ the auto-derived code (``MAJOR*1_000_000 + MINOR*10_000 + PATCH*100 +
659
+ build``) when the overlay says ``"auto"`` (``version_code_auto`` records
660
+ which). ``compile_sdk`` defaults to ``target_sdk`` at parse time.
661
+ """
662
+
663
+ schema_version: int
664
+ package: str
665
+ version_code: int = 1
666
+ version_code_auto: bool = False
667
+ build: int = 0
668
+ min_sdk: int = DEFAULT_ANDROID_MIN_SDK
669
+ target_sdk: int = DEFAULT_ANDROID_TARGET_SDK
670
+ compile_sdk: int = DEFAULT_ANDROID_TARGET_SDK
671
+ kivy_generation: int = DEFAULT_ANDROID_KIVY_GENERATION
672
+ abis: tuple[str, ...] = DEFAULT_ANDROID_ABIS
673
+ extra_index_urls: tuple[str, ...] = ()
674
+ find_links: tuple[str, ...] = ()
675
+ exclude: tuple[str, ...] = ()
676
+ base_theme: str = DEFAULT_ANDROID_BASE_THEME
677
+ python: AndroidPythonConfig | None = None
678
+ permissions: AndroidPermissions = field(default_factory=AndroidPermissions)
679
+ icons: AndroidIconConfig = field(default_factory=AndroidIconConfig)
680
+ splash: AndroidSplashConfig = field(default_factory=AndroidSplashConfig)
681
+ aars: tuple[AndroidArchiveDep, ...] = ()
682
+ jars: tuple[AndroidArchiveDep, ...] = ()
683
+ gradle: AndroidGradleConfig = field(default_factory=AndroidGradleConfig)
684
+ include_files: tuple[AndroidIncludeFile, ...] = ()
685
+ src: AndroidSrcConfig = field(default_factory=AndroidSrcConfig)
686
+ services: tuple[AndroidService, ...] = ()
687
+ activities: tuple[AndroidActivity, ...] = ()
688
+ intent_filters: tuple[AndroidIntentFilter, ...] = ()
689
+ manifest: AndroidManifestConfig = field(default_factory=AndroidManifestConfig)
690
+ signing: AndroidSigningConfig = field(default_factory=AndroidSigningConfig)
691
+ gradle_properties: dict[str, object] = field(default_factory=dict)
692
+ build_settings: AndroidBuildSettings = field(default_factory=AndroidBuildSettings)
693
+
694
+ @property
695
+ def python_required(self) -> AndroidPythonConfig:
696
+ if self.python is None: # pragma: no cover - loader enforces presence
697
+ raise RuntimeError("AndroidConfig.python is None despite loader rule 11")
698
+ return self.python
699
+
700
+
701
+ @dataclass(frozen=True)
702
+ class DesktopConfig:
703
+ """``[tool.kivy.linux.desktop]`` — freedesktop ``.desktop`` entry options."""
704
+
705
+ categories: tuple[str, ...] = DEFAULT_DESKTOP_CATEGORIES
706
+
707
+
708
+ @dataclass(frozen=True)
709
+ class LinuxConfig:
710
+ """``[tool.kivy.linux]`` overlay (linux-spec).
711
+
712
+ ``archs`` drives which manylinux wheel tags and per-arch runtime the lock
713
+ must cover. ``glibc_floor`` is ``None`` when unset, meaning the bundled
714
+ runtime's own glibc floor (2.17) applies; setting it higher admits
715
+ newer-manylinux-only wheels at the cost of raising the artifact's host floor.
716
+ """
717
+
718
+ schema_version: int
719
+ app_id: str
720
+ glibc_floor: str | None = None
721
+ archs: tuple[str, ...] = DEFAULT_LINUX_ARCHS
722
+ extra_index_urls: tuple[str, ...] = ()
723
+ find_links: tuple[str, ...] = ()
724
+ exclude: tuple[str, ...] = ()
725
+ python_version: str | None = None
726
+ icons: IconConfig = field(default_factory=IconConfig)
727
+ desktop: DesktopConfig = field(default_factory=DesktopConfig)
728
+ binaries: tuple[NativeBinaryDep, ...] = ()
729
+ build_settings: DesktopBuildSettings = field(default_factory=DesktopBuildSettings)
730
+
731
+
732
+ @dataclass(frozen=True)
733
+ class KivyMeta:
734
+ """Cross-platform ``[tool.kivy]`` table."""
735
+
736
+ app_dir: str
737
+ display_name: str | None = None
738
+ entry_point: str = DEFAULT_ENTRY_POINT
739
+ orientation: tuple[str, ...] = DEFAULT_ORIENTATION
740
+
741
+
742
+ @dataclass(frozen=True)
743
+ class Config:
744
+ """Fully-validated kivyforge project configuration."""
745
+
746
+ project: ProjectMeta
747
+ kivy: KivyMeta
748
+ ios: IosConfig | None = None
749
+ macos: MacosConfig | None = None
750
+ linux: LinuxConfig | None = None
751
+ windows: WindowsConfig | None = None
752
+ android: AndroidConfig | None = None
753
+
754
+ @property
755
+ def display_name(self) -> str:
756
+ if self.kivy.display_name:
757
+ return self.kivy.display_name
758
+ return self.project.name
759
+
760
+ @property
761
+ def app_slug(self) -> str:
762
+ """The Xcode target / folder slug derived from [project].name."""
763
+ return self.project.name
764
+
765
+ @property
766
+ def ios_required(self) -> IosConfig:
767
+ """``[tool.kivy.ios]`` after ``load_config(..., require_ios=True)``.
768
+
769
+ Pyright cannot infer that ``ios`` is set from the loader alone; iOS CLI
770
+ verbs should use this accessor instead of ``config.ios`` directly.
771
+ """
772
+ if self.ios is None:
773
+ raise RuntimeError(
774
+ "Config.ios is None; call load_config with require_ios=True "
775
+ "before accessing ios_required."
776
+ )
777
+ return self.ios
778
+
779
+ @property
780
+ def macos_required(self) -> MacosConfig:
781
+ """``[tool.kivy.macos]`` after ``load_config(..., require_macos=True)``."""
782
+ if self.macos is None:
783
+ raise RuntimeError(
784
+ "Config.macos is None; call load_config with require_macos=True "
785
+ "before accessing macos_required."
786
+ )
787
+ return self.macos
788
+
789
+ @property
790
+ def linux_required(self) -> LinuxConfig:
791
+ """``[tool.kivy.linux]`` after ``load_config(..., require_linux=True)``."""
792
+ if self.linux is None:
793
+ raise RuntimeError(
794
+ "Config.linux is None; call load_config with require_linux=True "
795
+ "before accessing linux_required."
796
+ )
797
+ return self.linux
798
+
799
+ @property
800
+ def windows_required(self) -> WindowsConfig:
801
+ """``[tool.kivy.windows]`` after ``load_config(..., require_windows=True)``."""
802
+ if self.windows is None:
803
+ raise RuntimeError(
804
+ "Config.windows is None; call load_config with require_windows=True "
805
+ "before accessing windows_required."
806
+ )
807
+ return self.windows
808
+
809
+ @property
810
+ def android_required(self) -> AndroidConfig:
811
+ """``[tool.kivy.android]`` after ``load_config(..., require_android=True)``."""
812
+ if self.android is None:
813
+ raise RuntimeError(
814
+ "Config.android is None; call load_config with require_android=True "
815
+ "before accessing android_required."
816
+ )
817
+ return self.android