kivyforge 0.1.0__tar.gz

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 (289) hide show
  1. kivyforge-0.1.0/CHANGELOG.md +2878 -0
  2. kivyforge-0.1.0/LICENSE +21 -0
  3. kivyforge-0.1.0/MANIFEST.in +13 -0
  4. kivyforge-0.1.0/PKG-INFO +578 -0
  5. kivyforge-0.1.0/README.md +539 -0
  6. kivyforge-0.1.0/kivyforge/__init__.py +11 -0
  7. kivyforge-0.1.0/kivyforge/artifacts/__init__.py +41 -0
  8. kivyforge-0.1.0/kivyforge/artifacts/cache.py +81 -0
  9. kivyforge-0.1.0/kivyforge/artifacts/collect.py +424 -0
  10. kivyforge-0.1.0/kivyforge/artifacts/download.py +150 -0
  11. kivyforge-0.1.0/kivyforge/artifacts/frameworks.py +256 -0
  12. kivyforge-0.1.0/kivyforge/artifacts/include_files.py +538 -0
  13. kivyforge-0.1.0/kivyforge/artifacts/native_stage_util.py +374 -0
  14. kivyforge-0.1.0/kivyforge/artifacts/runtime.py +65 -0
  15. kivyforge-0.1.0/kivyforge/artifacts/verify.py +45 -0
  16. kivyforge-0.1.0/kivyforge/artifacts/wheels.py +92 -0
  17. kivyforge-0.1.0/kivyforge/build_outcome.py +111 -0
  18. kivyforge-0.1.0/kivyforge/bundle/__init__.py +1 -0
  19. kivyforge-0.1.0/kivyforge/bundle/app_metadata.py +334 -0
  20. kivyforge-0.1.0/kivyforge/bundle/casefold.py +88 -0
  21. kivyforge-0.1.0/kivyforge/bundle/entry_launcher.py +128 -0
  22. kivyforge-0.1.0/kivyforge/bundle/pycompile.py +323 -0
  23. kivyforge-0.1.0/kivyforge/bundle/workdir.py +131 -0
  24. kivyforge-0.1.0/kivyforge/capabilities.py +174 -0
  25. kivyforge-0.1.0/kivyforge/cli/__init__.py +55 -0
  26. kivyforge-0.1.0/kivyforge/cli/_common.py +171 -0
  27. kivyforge-0.1.0/kivyforge/cli/_output.py +164 -0
  28. kivyforge-0.1.0/kivyforge/cli/_platform.py +137 -0
  29. kivyforge-0.1.0/kivyforge/cli/_version.py +170 -0
  30. kivyforge-0.1.0/kivyforge/cli/build.py +168 -0
  31. kivyforge-0.1.0/kivyforge/cli/capabilities.py +63 -0
  32. kivyforge-0.1.0/kivyforge/cli/clean.py +179 -0
  33. kivyforge-0.1.0/kivyforge/cli/doctor.py +145 -0
  34. kivyforge-0.1.0/kivyforge/cli/init.py +863 -0
  35. kivyforge-0.1.0/kivyforge/cli/init_writer.py +773 -0
  36. kivyforge-0.1.0/kivyforge/cli/lock.py +472 -0
  37. kivyforge-0.1.0/kivyforge/cli/open_cmd.py +27 -0
  38. kivyforge-0.1.0/kivyforge/cli/package.py +199 -0
  39. kivyforge-0.1.0/kivyforge/cli/run.py +277 -0
  40. kivyforge-0.1.0/kivyforge/cli/status.py +96 -0
  41. kivyforge-0.1.0/kivyforge/cli/upgrade.py +354 -0
  42. kivyforge-0.1.0/kivyforge/config/__init__.py +47 -0
  43. kivyforge-0.1.0/kivyforge/config/errors.py +43 -0
  44. kivyforge-0.1.0/kivyforge/config/icons.py +60 -0
  45. kivyforge-0.1.0/kivyforge/config/keys.py +313 -0
  46. kivyforge-0.1.0/kivyforge/config/loader.py +4008 -0
  47. kivyforge-0.1.0/kivyforge/config/model.py +1091 -0
  48. kivyforge-0.1.0/kivyforge/config/version.py +124 -0
  49. kivyforge-0.1.0/kivyforge/doctor/__init__.py +19 -0
  50. kivyforge-0.1.0/kivyforge/doctor/checks_common.py +229 -0
  51. kivyforge-0.1.0/kivyforge/doctor/probe.py +544 -0
  52. kivyforge-0.1.0/kivyforge/doctor/result.py +71 -0
  53. kivyforge-0.1.0/kivyforge/host.py +29 -0
  54. kivyforge-0.1.0/kivyforge/lock/__init__.py +44 -0
  55. kivyforge-0.1.0/kivyforge/lock/_pip_shim.py +261 -0
  56. kivyforge-0.1.0/kivyforge/lock/find_links.py +202 -0
  57. kivyforge-0.1.0/kivyforge/lock/inputs.py +125 -0
  58. kivyforge-0.1.0/kivyforge/lock/model.py +113 -0
  59. kivyforge-0.1.0/kivyforge/lock/pep751.py +184 -0
  60. kivyforge-0.1.0/kivyforge/lock/pip_progress.py +74 -0
  61. kivyforge-0.1.0/kivyforge/lock/prefer.py +89 -0
  62. kivyforge-0.1.0/kivyforge/lock/reader.py +12 -0
  63. kivyforge-0.1.0/kivyforge/lock/resolver.py +142 -0
  64. kivyforge-0.1.0/kivyforge/lock/sources.py +513 -0
  65. kivyforge-0.1.0/kivyforge/lock/wheelruntime/__init__.py +70 -0
  66. kivyforge-0.1.0/kivyforge/lock/wheelruntime/builder.py +432 -0
  67. kivyforge-0.1.0/kivyforge/lock/wheelruntime/model.py +102 -0
  68. kivyforge-0.1.0/kivyforge/lock/wheelruntime/native_binaries.py +127 -0
  69. kivyforge-0.1.0/kivyforge/lock/wheelruntime/pbs_github.py +288 -0
  70. kivyforge-0.1.0/kivyforge/lock/wheelruntime/profile.py +105 -0
  71. kivyforge-0.1.0/kivyforge/lock/wheelruntime/resolver.py +331 -0
  72. kivyforge-0.1.0/kivyforge/lock/wheelruntime/runtime.py +171 -0
  73. kivyforge-0.1.0/kivyforge/lock/wheelruntime/serialize.py +270 -0
  74. kivyforge-0.1.0/kivyforge/net.py +21 -0
  75. kivyforge-0.1.0/kivyforge/platforms/__init__.py +114 -0
  76. kivyforge-0.1.0/kivyforge/platforms/android/__init__.py +258 -0
  77. kivyforge-0.1.0/kivyforge/platforms/android/adb.py +340 -0
  78. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/__init__.py +7 -0
  79. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/contract.py +215 -0
  80. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/render.py +408 -0
  81. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/android_pkg/__init__.py +81 -0
  82. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/android_pkg/config.py +27 -0
  83. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/android_pkg/loadingscreen.py +6 -0
  84. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +127 -0
  85. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +52 -0
  86. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
  87. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +183 -0
  88. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
  89. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/cpp/main.c +157 -0
  90. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
  91. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +319 -0
  92. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +135 -0
  93. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +271 -0
  94. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +255 -0
  95. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/LICENSE +21 -0
  96. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/NOTICE +11 -0
  97. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/P4A_VENDOR.toml +107 -0
  98. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/activity.py +214 -0
  99. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/broadcast.py +103 -0
  100. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/darkmode.py +57 -0
  101. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/permissions.py +621 -0
  102. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/runnable.py +58 -0
  103. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/android/storage.py +117 -0
  104. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/java/PythonActivity.changeKeyboard.sdl3.java.in +9 -0
  105. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/java/org/kivy/android/GenericBroadcastReceiver.java +19 -0
  106. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/p4a/java/org/kivy/android/GenericBroadcastReceiverCallback.java +9 -0
  107. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
  108. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
  109. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
  110. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
  111. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
  112. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
  113. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
  114. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2123 -0
  115. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
  116. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
  117. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
  118. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
  119. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
  120. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
  121. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
  122. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
  123. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
  124. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
  125. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
  126. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
  127. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
  128. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
  129. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
  130. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
  131. kivyforge-0.1.0/kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
  132. kivyforge-0.1.0/kivyforge/platforms/android/cli.py +1691 -0
  133. kivyforge-0.1.0/kivyforge/platforms/android/doctor.py +1310 -0
  134. kivyforge-0.1.0/kivyforge/platforms/android/elf.py +267 -0
  135. kivyforge-0.1.0/kivyforge/platforms/android/generate/__init__.py +1 -0
  136. kivyforge-0.1.0/kivyforge/platforms/android/generate/launcher.py +49 -0
  137. kivyforge-0.1.0/kivyforge/platforms/android/generate/manifest.py +378 -0
  138. kivyforge-0.1.0/kivyforge/platforms/android/generate/project.py +539 -0
  139. kivyforge-0.1.0/kivyforge/platforms/android/generate/services.py +234 -0
  140. kivyforge-0.1.0/kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
  141. kivyforge-0.1.0/kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
  142. kivyforge-0.1.0/kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
  143. kivyforge-0.1.0/kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
  144. kivyforge-0.1.0/kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
  145. kivyforge-0.1.0/kivyforge/platforms/android/gradlew.py +195 -0
  146. kivyforge-0.1.0/kivyforge/platforms/android/icons.py +223 -0
  147. kivyforge-0.1.0/kivyforge/platforms/android/lock/__init__.py +1 -0
  148. kivyforge-0.1.0/kivyforge/platforms/android/lock/archives.py +109 -0
  149. kivyforge-0.1.0/kivyforge/platforms/android/lock/builder.py +552 -0
  150. kivyforge-0.1.0/kivyforge/platforms/android/lock/generation.py +39 -0
  151. kivyforge-0.1.0/kivyforge/platforms/android/lock/markers.py +71 -0
  152. kivyforge-0.1.0/kivyforge/platforms/android/lock/maven.py +294 -0
  153. kivyforge-0.1.0/kivyforge/platforms/android/lock/model.py +106 -0
  154. kivyforge-0.1.0/kivyforge/platforms/android/lock/python_meta.py +226 -0
  155. kivyforge-0.1.0/kivyforge/platforms/android/lock/reader.py +195 -0
  156. kivyforge-0.1.0/kivyforge/platforms/android/lock/resolver.py +363 -0
  157. kivyforge-0.1.0/kivyforge/platforms/android/lock/writer.py +118 -0
  158. kivyforge-0.1.0/kivyforge/platforms/android/policy.py +240 -0
  159. kivyforge-0.1.0/kivyforge/platforms/android/sdk_packages.py +123 -0
  160. kivyforge-0.1.0/kivyforge/platforms/android/signing.py +155 -0
  161. kivyforge-0.1.0/kivyforge/platforms/android/smoke.py +46 -0
  162. kivyforge-0.1.0/kivyforge/platforms/android/splash.py +280 -0
  163. kivyforge-0.1.0/kivyforge/platforms/android/stage/__init__.py +1 -0
  164. kivyforge-0.1.0/kivyforge/platforms/android/stage/bundle.py +329 -0
  165. kivyforge-0.1.0/kivyforge/platforms/android/stage/conflicts.py +73 -0
  166. kivyforge-0.1.0/kivyforge/platforms/android/stage/jnilibs.py +332 -0
  167. kivyforge-0.1.0/kivyforge/platforms/android/stage/layouts.py +39 -0
  168. kivyforge-0.1.0/kivyforge/platforms/android/stage/runtime.py +61 -0
  169. kivyforge-0.1.0/kivyforge/platforms/android/stage/wheels.py +107 -0
  170. kivyforge-0.1.0/kivyforge/platforms/android/toolchain.py +41 -0
  171. kivyforge-0.1.0/kivyforge/platforms/base.py +226 -0
  172. kivyforge-0.1.0/kivyforge/platforms/ios/__init__.py +161 -0
  173. kivyforge-0.1.0/kivyforge/platforms/ios/assets.py +193 -0
  174. kivyforge-0.1.0/kivyforge/platforms/ios/buildsettings.py +315 -0
  175. kivyforge-0.1.0/kivyforge/platforms/ios/cli.py +788 -0
  176. kivyforge-0.1.0/kivyforge/platforms/ios/doctor.py +811 -0
  177. kivyforge-0.1.0/kivyforge/platforms/ios/entitlements.py +287 -0
  178. kivyforge-0.1.0/kivyforge/platforms/ios/generator.py +446 -0
  179. kivyforge-0.1.0/kivyforge/platforms/ios/includes.py +249 -0
  180. kivyforge-0.1.0/kivyforge/platforms/ios/legacy_module.py +94 -0
  181. kivyforge-0.1.0/kivyforge/platforms/ios/lock/__init__.py +79 -0
  182. kivyforge-0.1.0/kivyforge/platforms/ios/lock/builder.py +577 -0
  183. kivyforge-0.1.0/kivyforge/platforms/ios/lock/markers.py +69 -0
  184. kivyforge-0.1.0/kivyforge/platforms/ios/lock/model.py +74 -0
  185. kivyforge-0.1.0/kivyforge/platforms/ios/lock/python_meta.py +200 -0
  186. kivyforge-0.1.0/kivyforge/platforms/ios/lock/reader.py +173 -0
  187. kivyforge-0.1.0/kivyforge/platforms/ios/lock/resolver.py +388 -0
  188. kivyforge-0.1.0/kivyforge/platforms/ios/lock/spm.py +317 -0
  189. kivyforge-0.1.0/kivyforge/platforms/ios/lock/writer.py +86 -0
  190. kivyforge-0.1.0/kivyforge/platforms/ios/lock/xcframework.py +188 -0
  191. kivyforge-0.1.0/kivyforge/platforms/ios/materialize.py +67 -0
  192. kivyforge-0.1.0/kivyforge/platforms/ios/plist.py +91 -0
  193. kivyforge-0.1.0/kivyforge/platforms/ios/privacy.py +39 -0
  194. kivyforge-0.1.0/kivyforge/platforms/ios/skeleton.py +163 -0
  195. kivyforge-0.1.0/kivyforge/platforms/ios/sources.py +128 -0
  196. kivyforge-0.1.0/kivyforge/platforms/ios/staging.py +281 -0
  197. kivyforge-0.1.0/kivyforge/platforms/ios/swift_packages.py +324 -0
  198. kivyforge-0.1.0/kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +42 -0
  199. kivyforge-0.1.0/kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +225 -0
  200. kivyforge-0.1.0/kivyforge/platforms/ios/templates/kivyforge_native_modules.h.in +35 -0
  201. kivyforge-0.1.0/kivyforge/platforms/ios/templates/main.m +16 -0
  202. kivyforge-0.1.0/kivyforge/platforms/ios/templates/main_config.h.in +15 -0
  203. kivyforge-0.1.0/kivyforge/platforms/ios/xcframeworks.py +194 -0
  204. kivyforge-0.1.0/kivyforge/platforms/ios/xcode/__init__.py +80 -0
  205. kivyforge-0.1.0/kivyforge/platforms/ios/xcode/commands.py +329 -0
  206. kivyforge-0.1.0/kivyforge/platforms/ios/xcode/runner.py +424 -0
  207. kivyforge-0.1.0/kivyforge/platforms/linux/__init__.py +156 -0
  208. kivyforge-0.1.0/kivyforge/platforms/linux/appimage.py +229 -0
  209. kivyforge-0.1.0/kivyforge/platforms/linux/bundle.py +263 -0
  210. kivyforge-0.1.0/kivyforge/platforms/linux/cli.py +282 -0
  211. kivyforge-0.1.0/kivyforge/platforms/linux/desktop.py +73 -0
  212. kivyforge-0.1.0/kivyforge/platforms/linux/doctor.py +478 -0
  213. kivyforge-0.1.0/kivyforge/platforms/linux/elftools.py +95 -0
  214. kivyforge-0.1.0/kivyforge/platforms/linux/icons.py +117 -0
  215. kivyforge-0.1.0/kivyforge/platforms/linux/launcher.py +136 -0
  216. kivyforge-0.1.0/kivyforge/platforms/linux/lock/__init__.py +136 -0
  217. kivyforge-0.1.0/kivyforge/platforms/linux/lock/profile.py +254 -0
  218. kivyforge-0.1.0/kivyforge/platforms/linux/lock/runtime.py +81 -0
  219. kivyforge-0.1.0/kivyforge/platforms/linux/native_stage.py +47 -0
  220. kivyforge-0.1.0/kivyforge/platforms/linux/runtime_stage.py +128 -0
  221. kivyforge-0.1.0/kivyforge/platforms/linux/wheels_stage.py +103 -0
  222. kivyforge-0.1.0/kivyforge/platforms/macos/__init__.py +142 -0
  223. kivyforge-0.1.0/kivyforge/platforms/macos/bundle.py +337 -0
  224. kivyforge-0.1.0/kivyforge/platforms/macos/cli.py +324 -0
  225. kivyforge-0.1.0/kivyforge/platforms/macos/doctor.py +604 -0
  226. kivyforge-0.1.0/kivyforge/platforms/macos/icns.py +84 -0
  227. kivyforge-0.1.0/kivyforge/platforms/macos/includes.py +97 -0
  228. kivyforge-0.1.0/kivyforge/platforms/macos/launcher.py +231 -0
  229. kivyforge-0.1.0/kivyforge/platforms/macos/lock/__init__.py +152 -0
  230. kivyforge-0.1.0/kivyforge/platforms/macos/lock/profile.py +119 -0
  231. kivyforge-0.1.0/kivyforge/platforms/macos/lock/runtime.py +63 -0
  232. kivyforge-0.1.0/kivyforge/platforms/macos/machotools.py +402 -0
  233. kivyforge-0.1.0/kivyforge/platforms/macos/native_links.py +289 -0
  234. kivyforge-0.1.0/kivyforge/platforms/macos/native_stage.py +330 -0
  235. kivyforge-0.1.0/kivyforge/platforms/macos/notarize.py +151 -0
  236. kivyforge-0.1.0/kivyforge/platforms/macos/plist.py +43 -0
  237. kivyforge-0.1.0/kivyforge/platforms/macos/runtime_stage.py +130 -0
  238. kivyforge-0.1.0/kivyforge/platforms/macos/signing.py +84 -0
  239. kivyforge-0.1.0/kivyforge/platforms/macos/swift_packages.py +287 -0
  240. kivyforge-0.1.0/kivyforge/platforms/macos/wheels_stage.py +108 -0
  241. kivyforge-0.1.0/kivyforge/platforms/windows/__init__.py +154 -0
  242. kivyforge-0.1.0/kivyforge/platforms/windows/assets.py +76 -0
  243. kivyforge-0.1.0/kivyforge/platforms/windows/bundle.py +330 -0
  244. kivyforge-0.1.0/kivyforge/platforms/windows/cli.py +427 -0
  245. kivyforge-0.1.0/kivyforge/platforms/windows/doctor.py +575 -0
  246. kivyforge-0.1.0/kivyforge/platforms/windows/fsswap.py +68 -0
  247. kivyforge-0.1.0/kivyforge/platforms/windows/icons.py +52 -0
  248. kivyforge-0.1.0/kivyforge/platforms/windows/launcher/__init__.py +198 -0
  249. kivyforge-0.1.0/kivyforge/platforms/windows/launcher/build_launcher.py +583 -0
  250. kivyforge-0.1.0/kivyforge/platforms/windows/launcher/launcher.c +379 -0
  251. kivyforge-0.1.0/kivyforge/platforms/windows/lock/__init__.py +122 -0
  252. kivyforge-0.1.0/kivyforge/platforms/windows/lock/profile.py +101 -0
  253. kivyforge-0.1.0/kivyforge/platforms/windows/lock/runtime.py +69 -0
  254. kivyforge-0.1.0/kivyforge/platforms/windows/naming.py +52 -0
  255. kivyforge-0.1.0/kivyforge/platforms/windows/native_stage.py +66 -0
  256. kivyforge-0.1.0/kivyforge/platforms/windows/pathdepth.py +60 -0
  257. kivyforge-0.1.0/kivyforge/platforms/windows/petools.py +98 -0
  258. kivyforge-0.1.0/kivyforge/platforms/windows/rcedit.py +121 -0
  259. kivyforge-0.1.0/kivyforge/platforms/windows/runtime_stage.py +242 -0
  260. kivyforge-0.1.0/kivyforge/platforms/windows/signing.py +173 -0
  261. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
  262. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/NOTICE +17 -0
  263. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
  264. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/TOOLSET.txt +5 -0
  265. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/__init__.py +12 -0
  266. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/fetch_rcedit.py +99 -0
  267. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
  268. kivyforge-0.1.0/kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
  269. kivyforge-0.1.0/kivyforge/platforms/windows/wheels_stage.py +139 -0
  270. kivyforge-0.1.0/kivyforge/report/__init__.py +31 -0
  271. kivyforge-0.1.0/kivyforge/report/console.py +231 -0
  272. kivyforge-0.1.0/kivyforge/report/diagnostics.py +300 -0
  273. kivyforge-0.1.0/kivyforge/report/envelope.py +79 -0
  274. kivyforge-0.1.0/kivyforge/report/exit_codes.py +56 -0
  275. kivyforge-0.1.0/kivyforge/report/failures.py +164 -0
  276. kivyforge-0.1.0/kivyforge/report/streams.py +71 -0
  277. kivyforge-0.1.0/kivyforge/report/transfers.py +209 -0
  278. kivyforge-0.1.0/kivyforge/report/verbosity.py +74 -0
  279. kivyforge-0.1.0/kivyforge/status.py +179 -0
  280. kivyforge-0.1.0/kivyforge/streaming.py +92 -0
  281. kivyforge-0.1.0/kivyforge/versioning.py +285 -0
  282. kivyforge-0.1.0/kivyforge.egg-info/PKG-INFO +578 -0
  283. kivyforge-0.1.0/kivyforge.egg-info/SOURCES.txt +287 -0
  284. kivyforge-0.1.0/kivyforge.egg-info/dependency_links.txt +1 -0
  285. kivyforge-0.1.0/kivyforge.egg-info/entry_points.txt +3 -0
  286. kivyforge-0.1.0/kivyforge.egg-info/requires.txt +17 -0
  287. kivyforge-0.1.0/kivyforge.egg-info/top_level.txt +1 -0
  288. kivyforge-0.1.0/pyproject.toml +180 -0
  289. kivyforge-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,2878 @@
1
+ # Changelog
2
+
3
+ ## [v0.1.0](https://github.com/ElliotGarbus/kivyforge/tree/v0.1.0) - 2026-11-02
4
+
5
+ ### Version numbers restart at 0.1.0
6
+
7
+ - **Changed:** the first public release of kivyforge is 0.1.0, a beta. The
8
+ earlier development builds were numbered 3.0.0.dev0 and 3.0.0.dev1, which read
9
+ as tied to Kivy 3.0, but Android can use Kivy 2.3.1 and the desktop targets do.
10
+ Both development builds are yanked from PyPI.
11
+ - **Migration:** if you installed a development build, ask for this version by
12
+ number, because 0.1.0 sorts below 3.0.0.dev1 and an upgrade command can stay on
13
+ the development build. `kivyforge doctor` does not warn about it, for the same
14
+ reason. Run `pip install kivyforge==0.1.0`, or
15
+ `uv tool install --force kivyforge==0.1.0`. Existing projects and locks keep
16
+ working.
17
+ - **Known issues:** on iOS 27, rotation breaks a Kivy app's layout
18
+ ([#195](https://github.com/ElliotGarbus/kivyforge/issues/195)), and the current
19
+ Kivy iOS wheel can show blank text or crash KivyMD
20
+ ([kivy/kivy#9443](https://github.com/kivy/kivy/pull/9443)). See
21
+ [Known issues on iOS](docs/guides/troubleshooting/index.md#known-issues-on-ios).
22
+
23
+ ### Upgrading kivyforge no longer makes `lock --check` fail
24
+
25
+ - **Fixed:** `lock --check` compared the kivyforge version each lock records, so
26
+ every kivyforge upgrade reported every lock as stale, with an empty diff. It now
27
+ ignores that version on every platform. A real difference in the lock still
28
+ fails the check.
29
+ - **Migration:** none needed. You don't have to re-lock. The next re-lock updates
30
+ the recorded version.
31
+
32
+ ### `init` seeds CPython 3.15.0 for iOS
33
+
34
+ - **Changed:** `kivyforge init -p ios` writes `version = "3.15.0"` in
35
+ `[tool.kivy.ios.python]`. It still wrote the `3.15.0b4` pre-release after the
36
+ examples moved to the final release.
37
+ - **Fixed:** the comment `init -p android` writes on `extra_index_urls` no longer
38
+ says the index serves pyjnius, which publishes its Android wheels on PyPI.
39
+ - **Migration:** none needed. An existing project keeps the version it pins, and
40
+ `init --force` preserves it. To move from `3.15.0b4`, set the version and run
41
+ `kivyforge lock -p ios`.
42
+
43
+ ### Docs: the README and guides checked against the tool before the beta
44
+
45
+ - **Fixed:** the README's iOS quick start left out `-p ios`, so on a Mac it
46
+ configured and built macOS instead. Its sample configuration lacked the
47
+ required `schema_version`.
48
+ - **Fixed:** the guides and FAQ said kivyforge ignores unknown keys, that a new
49
+ upstream release can fail `lock --check`, that a plain `lock` takes the newest
50
+ versions, that `run` takes `--no-verify-lock`, and that the Android pyjnius
51
+ wheel comes from the mobile wheel index. kivyforge rejects unknown keys,
52
+ `lock` and `lock --check` keep the locked versions, `run` has no such flag, and
53
+ pyjnius comes from PyPI. The desktop quickstart no longer asks for `linux` and
54
+ `windows` extras that don't exist, or for an `aarch64` Linux build host, which
55
+ kivyforge refuses.
56
+ - **Changed:** the install commands no longer use `--pre`. pip and pipx take a
57
+ pre-release when a package has no stable release, and after one, `--pre` would
58
+ choose an older development release. iOS examples in the guides use CPython
59
+ 3.15.0, the version the iOS examples pin.
60
+ - **Added:** which CPython versions Kivy's wheels support on each target, how to
61
+ report a problem, and *Add a Swift package to your macOS app* in the site's
62
+ navigation, where it was missing.
63
+
64
+ ### Docs and examples: Rubicon-ObjC is the documented bridge to Objective-C
65
+
66
+ - **Changed:** the iOS guides and examples call Objective-C through
67
+ [Rubicon-ObjC](https://github.com/beeware/rubicon-objc). A new guide, *Call
68
+ iOS APIs with Rubicon-ObjC*, comes before the Swift guides; *Call iOS APIs
69
+ with pyobjus* is now *Use pyobjus*, for apps that already use it or depend
70
+ on a package that does, such as plyer's iOS facades. pyobjus stays
71
+ supported, and the two can be used in one app.
72
+ - **Changed:** `keychain-spm`, `ios-lifecycle-hook` and `ios-webview` use
73
+ Rubicon-ObjC. `ios-webview` and the web-view recipe in *Replace the
74
+ kivy-ios ios module* need no `ctypes` any more, and the example's
75
+ **Open (pyobjus)** and **Open (ctypes)** buttons are one **Open**.
76
+ - **Renamed:** `pyobjus-ball` is now `rubicon-ball`, with its `ctypes` calls
77
+ replaced by Rubicon-ObjC, and `pyobjus-deviceinfo` is now
78
+ `rubicon-deviceinfo`. Their bundle IDs changed with them, so each installs
79
+ as a new app beside the old one.
80
+ - **Migration:** nothing in kivyforge changes. An app that copied
81
+ `lifecycle.py` from `ios-lifecycle-hook` keeps working with pyobjus; to
82
+ move it, take the new `lifecycle.py` and replace `pyobjus` with
83
+ `rubicon-objc>=0.5.7` in `[project].dependencies`, then re-lock.
84
+
85
+ ### iOS rebuilds drop a removed wheel's xcframeworks
86
+
87
+ - **Fixed:** after a wheel that ships xcframeworks is removed from the iOS
88
+ lock, such as kivy with `SDL3.xcframework`, `libEGL.xcframework` and the
89
+ rest, the next `build -p ios` no longer links and embeds them. `build` only
90
+ ever added to `<app>-ios/Frameworks/`, so they stayed there, and the Xcode
91
+ project kept every one in Link Binary With Libraries and Embed Frameworks
92
+ until `kivyforge clean`. This affected Simulator and `--device` builds, and
93
+ `package -p ios` too: an archive starts from an empty app, but still embeds
94
+ what the project references. `build` now rebuilds `Frameworks/` from the
95
+ lock each time, so the same applies to a removed
96
+ `[tool.kivy.ios.native.xcframeworks]` entry. Xcode then removes the embedded
97
+ copies from an incremental `.app` itself.
98
+ - **Changed:** `build` now also removes `KIVYFORGE_REQUIRES_SDL=1` from the
99
+ project's `GCC_PREPROCESSOR_DEFINITIONS` when SDL3 is not staged. It was
100
+ only ever added, and with SDL3 now leaving with kivy, the next build would
101
+ have stopped at `Kivy app selected (KIVYFORGE_REQUIRES_SDL) but SDL3 headers
102
+ were not found`. Other defines in that setting, including ones added in
103
+ Xcode, are kept.
104
+ - **Migration:** none needed. The next `build` clears what the removed wheel
105
+ left in an existing project.
106
+
107
+ ### macOS: `[tool.kivy.macos.info_plist]`, and the app is its own main bundle
108
+
109
+ - **Added:** `[tool.kivy.macos.info_plist]`, a table of keys merged into the
110
+ `.app`'s `Info.plist`: usage descriptions, URL schemes, or an SDK's keys
111
+ such as Firebase's `FirebaseAppDelegateProxyEnabled`
112
+ ([#201](https://github.com/ElliotGarbus/kivyforge/issues/201)). Keys
113
+ kivyforge writes, such as `CFBundleIdentifier` and `LSMinimumSystemVersion`,
114
+ are rejected; the reference lists them.
115
+ - **Fixed:** inside a running macOS app, the main bundle was
116
+ `Contents/Resources/python/bin`, not the `.app`, because the launcher
117
+ replaced itself with `python3`. Code that looks in the main bundle found no
118
+ bundle identifier, no `Info.plist` keys and no `Contents/Resources`, so
119
+ Firebase could not find a `GoogleService-Info.plist` added with
120
+ `include_files`. The launcher now runs Python in its own process from the
121
+ runtime's `libpython`, so the main bundle is the `.app`. `sys.executable`
122
+ is still the bundled `python3`.
123
+ - **Changed:** the `.app`'s entitlements now apply to the process Python runs
124
+ in. Before, they stayed with the launcher, and Python ran without them. For
125
+ a library your app loads with `ctypes`, `@executable_path` is now
126
+ `Contents/MacOS`, so Apple's `@executable_path/../Frameworks/<lib>` form
127
+ works instead of failing the build with `KF-MACOS-NATIVE-LOAD-PATH`.
128
+ `@rpath` links resolve where they did before.
129
+ - **Migration:** run `kivyforge build -p macos` again; nothing in
130
+ `pyproject.toml` needs to change. Swift packages on macOS, the other half
131
+ of #201, are in the next entry.
132
+
133
+ ### macOS: Swift packages (`[tool.kivy.macos.native.swift_packages]`)
134
+
135
+ - **Added:** `[tool.kivy.macos.native.swift_packages]`, with the iOS keys
136
+ `url`, `path`, `requirement` and `products`
137
+ ([#201](https://github.com/ElliotGarbus/kivyforge/issues/201)). `lock -p
138
+ macos` pins each remote package's revision in
139
+ `[[tool.kivyforge.swift_packages]]` in `pylock.macos.toml`. `build` builds
140
+ every product with `xcodebuild` into
141
+ `Contents/Frameworks/KivyforgePackages.framework`, plus a framework for each
142
+ product a package declares `type: .dynamic`, and stages each package's
143
+ resource bundle in `Contents/Resources/`, where `Bundle.module` finds it.
144
+ Python loads the framework with Rubicon-ObjC's `load_library` and calls its
145
+ `@objc` classes; the guide is "Add a Swift package to your macOS app".
146
+ Building needs the full Xcode; `doctor -p macos` checks for it.
147
+ - **Refused:** `link` and `embed` under the macOS table. They are Xcode
148
+ target settings, and a macOS `.app` has no Xcode target.
149
+ - **Migration:** `swift_packages` is now a macOS lock input, so every existing
150
+ `pylock.macos.toml` reads as stale once, with or without the table. Run
151
+ `kivyforge lock -p macos`; it keeps the pinned versions and runtime.
152
+
153
+ ### iOS rebuilds drop a removed dependency's frameworks
154
+
155
+ - **Fixed:** after a dependency with an extension module is removed and the
156
+ project re-locked, the next iOS Simulator or `--device` build no longer
157
+ keeps its `Frameworks/<package>.<module>.framework` from the previous build
158
+ in the `.app`. Before this change only `kivyforge clean` removed it. The
159
+ Build Python phase now removes every framework that python.org's
160
+ `install_python` made for a module this build no longer installs. Python,
161
+ SDL3, Swift package and your own frameworks are not touched. `package -p ios`
162
+ was not affected, because an archive starts from an empty app.
163
+ - **Fixed:** removing an iOS project's last dependency no longer leaves the
164
+ previous build's packages in `pip-deps/`. With nothing to install, `build`
165
+ had skipped clearing the slice along with pip.
166
+ - **Migration:** none needed. The next `build` replaces the Build Python phase
167
+ in an existing generated project. A stale framework from an earlier build is
168
+ removed by that build.
169
+
170
+ ### `lock` notices a rebuilt wheel in `find_links`
171
+
172
+ - **Fixed:** replacing a locked wheel in a `find_links` directory with a new
173
+ build of the same filename, such as a wheel built from a newer commit on
174
+ GitHub, no longer leaves a lock that `lock` calls in sync and `build`
175
+ rejects with a SHA-256 mismatch
176
+ ([#190](https://github.com/ElliotGarbus/kivyforge/issues/190)). `lock` now
177
+ names the changed or missing wheel and resolves that package again, and
178
+ every other package keeps its locked version. `lock --check` names the wheel
179
+ instead of failing with an empty list of changes.
180
+ - **Fixed:** a macOS, Linux, or Windows lock no longer lists a package's
181
+ optional (extra) and other-platform requirements, such as KivyMD's `pytest`
182
+ and `sphinx`, in that package's `dependencies`. Only that field changed:
183
+ those packages were never locked or installed. Android and iOS locks already
184
+ recorded only the requirements that apply.
185
+ - **Added:** the guide
186
+ [Build a wheel from a GitHub repository](docs/guides/guides/cross-platform/wheel-from-github.md),
187
+ with KivyMD as the example.
188
+ - **Migration:** none needed. An existing desktop lock still passes
189
+ `lock --check`: a package whose wheels are unchanged keeps the
190
+ `dependencies` it was locked with. The lists shrink the next time `lock`
191
+ writes the file.
192
+
193
+ ### `exclude` stops pip from looking for the package
194
+
195
+ - **Fixed:** a name in `exclude` is no longer resolved at all, on every
196
+ platform ([#190](https://github.com/ElliotGarbus/kivyforge/issues/190)).
197
+ `lock` used to let pip resolve the whole graph and drop excluded packages
198
+ afterwards, so an excluded dependency with no wheel for the target, such as
199
+ KivyMD's `pycairo` on iOS, still failed the lock. pip now never follows a
200
+ dependency on an excluded name, so it never downloads or builds it.
201
+ - **Changed:** what only an excluded package needed leaves the lock with it.
202
+ Before, an excluded package's own dependencies were still locked and
203
+ installed. If your app imports one of them directly, add it to
204
+ `[project].dependencies`. Re-run `kivyforge lock` to apply this to an
205
+ existing lock; `lock --check` reports the packages it removes.
206
+
207
+ ### iOS: `OTHER_LDFLAGS` from `[tool.kivy.ios.xcode.build_settings]` reaches the project
208
+
209
+ - **Fixed:** an `OTHER_LDFLAGS` set under
210
+ `[tool.kivy.ios.xcode.build_settings]`, such as the
211
+ `"$(inherited) -ObjC"` Firebase requires, was silently replaced by
212
+ kivyforge's own `-framework UIKit` and never reached the Xcode project
213
+ ([#197](https://github.com/ElliotGarbus/kivyforge/issues/197)). Your flags
214
+ are now appended to kivyforge's, with `$(inherited)` once at the front if
215
+ you wrote it: `$(inherited) -framework UIKit -ObjC`.
216
+ `GCC_PREPROCESSOR_DEFINITIONS` had the same problem in Kivy apps, where
217
+ kivyforge sets `KIVYFORGE_REQUIRES_SDL=1`. It now merges the same way.
218
+ - **Migration:** none needed. Run `kivyforge build -p ios` again to write the
219
+ merged value into an existing project.
220
+
221
+ ### iOS: build settings kivyforge writes are rejected, not silently replaced
222
+
223
+ - **Changed:** `[tool.kivy.ios.xcode.build_settings]` accepted six keys that
224
+ kivyforge also writes, then replaced your value with its own without a word.
225
+ `PRODUCT_NAME`, `SDKROOT`, `ALWAYS_SEARCH_USER_PATHS` and `COPY_PHASE_STRIP`
226
+ are now reserved, like `INFOPLIST_FILE`. `ASSETCATALOG_COMPILER_APPICON_NAME`
227
+ is rejected while `[tool.kivy.ios.icons].source` is set, and
228
+ `CODE_SIGN_ENTITLEMENTS` while `[tool.kivy.ios.entitlements]` is set; without
229
+ those, you can still set them yourself.
230
+ - **Migration:** if config loading now fails naming one of these keys, delete
231
+ it from `[tool.kivy.ios.xcode.build_settings]`. It never took effect, so the
232
+ built app does not change. To use your own entitlements file or icon set,
233
+ remove `[tool.kivy.ios.entitlements]` or `[tool.kivy.ios.icons].source`
234
+ instead.
235
+
236
+ ### Android: `doctor` checks emulator acceleration on Windows, and `/dev/kvm` access on Linux
237
+
238
+ - **Fixed:** on Windows, `kivyforge doctor -p android` said the emulator was
239
+ accelerated without checking. It now asks the emulator itself
240
+ (`emulator -accel-check`, the same SDK emulator that lists your AVDs), so a
241
+ host without WHPX or AEHD gets a WARN that carries the emulator's own reason
242
+ and a hint naming both. If the emulator cannot answer (it times out, crashes
243
+ or will not start), the check passes as
244
+ `N AVD(s); acceleration not verified (<reason>)` rather than claiming
245
+ acceleration or warning about a problem nobody found.
246
+ - **Fixed:** on Linux the check passed whenever `/dev/kvm` existed, even when
247
+ your user could not open it, which is the usual state on a hosted CI runner
248
+ and makes the emulator refuse to start. It now warns
249
+ "AVDs present (N) but this user cannot open /dev/kvm", with how to join the
250
+ `kvm` group or, on CI, the udev rule that opens the device.
251
+ - **Migration:** none needed. A Windows host without acceleration, or a Linux
252
+ user without access to `/dev/kvm`, now gets a WARN where it got a PASS.
253
+ doctor's exit status does not change, since only a FAIL makes it non-zero.
254
+
255
+ ### iOS: stripped release builds with wheels that ship data files
256
+
257
+ - **Fixed:** `package -p ios` no longer fails on a wheel that installs data
258
+ files (setuptools `data_files`, the wheel's `.data/data/` tree). `pip
259
+ install --target` put them at the root of `pip-deps/`, and the release build
260
+ byte-compiled them with everything else, so one unimportable file stopped the
261
+ whole package. pyobjus is the case that hit it: its wheel ships Python 2
262
+ examples under `share/pyobjus-examples/`, so no app using pyobjus could
263
+ package with stripping on. That was hidden until CPython 3.15.0 final, because
264
+ before then iOS builds never byte-compiled.
265
+ - **Changed:** iOS apps no longer ship a wheel's data files at all, which is
266
+ what macOS has always done. Nothing on iOS could find them: the app's
267
+ `sys.prefix` is not `pip-deps/`. If your app reads a file a dependency
268
+ installs this way, add it with `include_files` instead.
269
+ - **Changed:** pip no longer byte-compiles `pip-deps/` during `build`. It used
270
+ the host's CPython, whose `.pyc` the app's CPython could never load; a
271
+ release build still compiles `pip-deps/` itself with a matching interpreter.
272
+
273
+ ### iOS: call Swift from Python
274
+
275
+ - **Added:** the `swift-bridge` example and the guide
276
+ [Call Swift from Python](docs/guides/guides/ios/swift.md), the answer to
277
+ [#181](https://github.com/ElliotGarbus/kivyforge/issues/181). A local Swift
278
+ package of `@objc` classes, declared in
279
+ `[tool.kivy.ios.native.swift_packages]`, is called from Python through
280
+ [Rubicon-ObjC](https://github.com/beeware/rubicon-objc), whose pure-Python
281
+ wheel comes from PyPI. The guide covers objects with state, collections in
282
+ both directions, methods that throw, Swift `async` methods called with a
283
+ Python completion handler, long work that Python polls from Kivy's `Clock`
284
+ to show progress and cancel, and a Swift delegate protocol implemented in
285
+ Python. A short section says what changes for an app that calls the same
286
+ shim with pyobjus, and [Call iOS APIs with pyobjus](docs/guides/guides/ios/pyobjus.md)
287
+ now lists the pyobjus 1.2.5 defects a Swift shim meets, with their
288
+ workarounds. kivyforge itself is unchanged, so nothing needs migrating.
289
+
290
+ ### iOS: run code in the app lifecycle
291
+
292
+ - **Added:** the `ios-lifecycle-hook` example and the guide
293
+ [Run code in the iOS app lifecycle](docs/guides/guides/ios/lifecycle-hooks.md),
294
+ the iOS answer to
295
+ [#85](https://github.com/ElliotGarbus/kivyforge/issues/85). A local
296
+ Objective-C package declared in `[tool.kivy.ios.native.swift_packages]`
297
+ runs before Python and adds the app-delegate methods SDL does not provide:
298
+ the push device token, background pushes, notification taps, background
299
+ tasks, and the URL that launched the app, which Kivy's `on_drop_file` never
300
+ receives. A Python module delivers them as Kivy events. kivyforge itself
301
+ is unchanged, so nothing needs migrating.
302
+
303
+ ### Android smoke test runs on one device
304
+
305
+ - **Fixed:** `run -p android --smoke` runs the contract test only on the device
306
+ it resolved. Gradle's connected-test task runs on every attached device
307
+ unless `ANDROID_SERIAL` is set, and the probe is built for the resolved
308
+ device's ABI only, so `--smoke --device` with an emulator also running
309
+ failed on the emulator even when the phone passed. No migration is needed.
310
+
311
+ ### Android: your own activity class as the launcher (`main_activity`)
312
+
313
+ - **Added:** `[tool.kivy.android].main_activity` names your own subclass of
314
+ `org.kivy.android.PythonActivity` as the app's launcher, so Java can run in
315
+ the activity's `onCreate` — where some SDKs must be set up — without
316
+ editing generated files. It is the counterpart of buildozer's
317
+ `android.entrypoint`, and `kivyforge init` now maps that key to it. The
318
+ manifest, `run`, the `--smoke` contract test and the release manifest
319
+ policy all follow the configured class. Python sees the subclass where it
320
+ always saw the activity (`android.mActivity`, `PythonActivity.mActivity`),
321
+ and the bundled `android` package works unchanged.
322
+ - **Added:** with `main_activity` set, `kivyforge build` fails with javac's
323
+ error (`KF-BUILD-TOOL-FAILED`, exit 5) if the class is missing, abstract,
324
+ not a `PythonActivity`, or has no public no-argument constructor. A value
325
+ that is not a fully qualified top-level class name, or is inside a package
326
+ the bootstrap generates (`org.kivy.android`, `org.libsdl.app`, `org.jnius`,
327
+ `org.renpy.android`), fails the configuration check (exit 1).
328
+ - **Changed:** the release manifest policy's exported baseline is the
329
+ configured launcher, not always `org.kivy.android.PythonActivity`, and the
330
+ single `LAUNCHER` activity must be that class. No migration for a project
331
+ without the key: its launcher is still `PythonActivity`.
332
+ - **Changed:** the `--smoke` contract test starts the activity the manifest
333
+ marks `LAUNCHER` instead of naming `PythonActivity`, and asserts on the
334
+ device that the running activity is the configured launcher.
335
+ - New example: `examples/mobile/android-activity-class`, a Java launcher
336
+ subclass beside a `ContentProvider` that registers activity lifecycle
337
+ callbacks (the route for a library), with a tracked lock as an on-device
338
+ gate.
339
+ - **Docs:** [Add Java libraries to your Android app](docs/guides/guides/android/java-libraries.md)
340
+ says what the subclass can use: it is a plain `Activity`, never an
341
+ `AppCompatActivity`, `FragmentActivity` or `ComponentActivity`. AndroidX
342
+ helpers that take a `Context` or `Activity` work, App Startup and
343
+ `ProcessLifecycleOwner` work once declared as dependencies, and
344
+ `registerForActivityResult`, `ViewModelProvider(this)` and SDKs such as
345
+ `BiometricPrompt` do not compile against it. It also shows App Startup's
346
+ manifest entry and how to choose permission request codes that don't
347
+ collide with `android.permissions`
348
+ ([#184](https://github.com/ElliotGarbus/kivyforge/issues/184)).
349
+ - **Docs:** the same guide's new section "Host an SDK that needs an AndroidX
350
+ activity" shows how to run SDKs that need a `ComponentActivity`,
351
+ `FragmentActivity` or `AppCompatActivity`, such as Stripe's PaymentSheet or
352
+ `BiometricPrompt`. They go in a second activity of your own, which Python
353
+ starts and reads the result from, with no `main_activity` and no bootstrap
354
+ change. It notes that PaymentSheet needs `compile_sdk = 36` and covers two
355
+ pyjnius traps: a Python `str` passed to `putExtra` arrives as a `char[]`,
356
+ and `setClassName` needs the package name.
357
+
358
+ ### Android: `[tool.kivy.android.src]` compiles your sources
359
+
360
+ - **Fixed:** Java and Kotlin roots listed under `[tool.kivy.android.src]` are
361
+ compiled into the app. Before, the generated `app/build.gradle` named each
362
+ root as written, Gradle resolved it against the generated `app/` module
363
+ where no such directory exists, and the build succeeded with none of your
364
+ classes in the APK — so a class reached through pyjnius failed at runtime
365
+ with `ClassNotFoundException`. The roots are now written relative to the
366
+ module and compiled in place, so the next build picks up an edit without a
367
+ copy step.
368
+ - **Changed:** each `src.java` / `src.kotlin` root must be an existing
369
+ directory; a missing one fails the configuration check (exit 1) instead of
370
+ building an app without the classes. No migration: a root that does not
371
+ exist never contributed anything.
372
+
373
+ ### Manifest-merger directives in Android raw-XML fragments
374
+
375
+ - **Added:** the generated `AndroidManifest.xml` declares the `tools`
376
+ namespace, so a raw-XML fragment in `[tool.kivy.android.manifest]` can carry
377
+ manifest-merger directives as Android's documentation writes them — App
378
+ Startup's `<provider ... tools:node="merge">` entry, or
379
+ `tools:node="remove"` to drop a component a library's manifest contributes.
380
+ Previously such a fragment failed as "not well-formed XML".
381
+ - **Changed:** the `application` / `activity` attribute tables reject a key
382
+ in any namespace other than `android:`, with a message saying where a
383
+ merger directive belongs. Before, such a key failed with the same
384
+ well-formedness error, so no configuration that loaded yesterday fails
385
+ today. A project that uses no `tools:` attribute produces a byte-identical
386
+ merged manifest: the merger consumes the namespace and emits nothing from
387
+ it.
388
+ ### macOS: native binaries go where Apple says code goes
389
+
390
+ - **Changed (breaking):** `[tool.kivy.macos.native.binaries]` now places
391
+ executables in `Contents/Helpers/` and dynamic libraries in
392
+ `Contents/Frameworks/`, instead of everything in `Contents/Resources/bin/`
393
+ ([#174](https://github.com/ElliotGarbus/kivyforge/issues/174)). Apple
394
+ reserves `Resources/` for data, and notarization and Gatekeeper are only
395
+ documented for code in code locations. Each location is flat, so a file
396
+ from an archive keeps only its name, and names that differ only in case
397
+ count as the same name. The launcher puts `Contents/Helpers` on `PATH`, so
398
+ helpers still run by name.
399
+ - **Changed (breaking):** the channel accepts native code only. A single-file
400
+ source that is not a Mach-O file fails the build with the new
401
+ `KF-MACOS-NATIVE-NOT-CODE`; ship data with `include_files` and scripts in
402
+ `app_dir`. Archive members that are not Mach-O, such as headers and
403
+ licences, are skipped with a warning under the same code. A plug-in
404
+ (`MH_BUNDLE`), any other Mach-O type, a universal file whose slices disagree
405
+ on type, and a binary inside a bundle in an archive (a `.framework`, for
406
+ instance) fail with the new `KF-MACOS-NATIVE-UNSUPPORTED`.
407
+ - **Added:** the build checks, per process, that links between declared
408
+ binaries resolve in the new layout, and fails with the new
409
+ `KF-MACOS-NATIVE-LOAD-PATH` when one won't, naming the `install_name_tool`
410
+ commands that fix it. A weak link, and a link that works only if your Python
411
+ code loads the target library first, are warnings under the same code. A
412
+ binary with no slice for the target architecture now fails the build;
413
+ before, only `doctor` noticed.
414
+ - **Changed:** signing now signs the main executable after all other code in
415
+ the bundle, since `codesign` treats `Helpers/` and `Frameworks/` as its
416
+ nested code.
417
+ - **Fixed:** two archive members whose names differ only in case no longer
418
+ silently overwrite each other on a case-insensitive filesystem; the build
419
+ refuses the archive before extracting it. This applies to Windows too.
420
+ - **Changed:** `doctor` checks the built app's `Contents/Helpers` and
421
+ `Contents/Frameworks` for the target architecture, reads the headers itself
422
+ instead of calling `lipo`, and checks that vendored single-file sources are
423
+ executables or dynamic libraries. `include_files` may now use
424
+ `Resources/bin`.
425
+ - **Migration:** load libraries from
426
+ `Path(sys.prefix).parents[1] / "Frameworks"` instead of
427
+ `Path(sys.prefix).parent / "bin"`; helpers run by name need no change. A
428
+ helper built to find its library beside it (`@executable_path/libx.dylib`,
429
+ `@loader_path/libx.dylib`, or an `@executable_path` run path) now fails the
430
+ build: relink it to `@rpath/libx.dylib` with the run path
431
+ `@executable_path/../Frameworks`, and give a library your Python code loads
432
+ the run path `@loader_path`. Move any data out of the channel and into
433
+ `include_files`. No re-lock is needed.
434
+
435
+ ### Android: plain `lock` accepts an edited `include_files` file
436
+
437
+ - **Fixed:** after you edited a file listed in
438
+ `[[tool.kivy.android.include_files]]`, or added or deleted a file in a
439
+ directory source, `build` told you to run `kivyforge lock -p android`, and
440
+ that command said the lock was already in sync and changed nothing
441
+ ([#176](https://github.com/ElliotGarbus/kivyforge/issues/176)). Only
442
+ `lock --update` got out, and it moved every package to its newest version.
443
+ Plain `lock` now re-hashes the include files and, if any changed, rewrites
444
+ just those hashes: nothing is re-resolved and no network is used.
445
+ - **Fixed:** `lock --check` names each changed include file in its diff,
446
+ instead of saying the lock is out of date and listing nothing, and
447
+ `status -p android` reports the lock as out of date when an include file has
448
+ drifted.
449
+ - **Changed:** an include that would replace the generated manifest or a
450
+ Gradle file is now reported as `KF-INCLUDE-RESERVED` instead of `KF-ERROR`.
451
+ The exit status is still 1 and the message is unchanged.
452
+ - **Migration:** a consumer that branched on `KF-ERROR` for the overwrite
453
+ refusal should branch on `KF-INCLUDE-RESERVED`. Nothing else is needed.
454
+
455
+ ### Choose which index each package comes from
456
+
457
+ - **Added:** name an index once in `[[tool.kivy.index]]` (`name`, `url`, and
458
+ optional `explicit`) and pin packages to it in `[tool.kivy.sources]`, or in
459
+ a target's own `sources` table, as `kivy = { index = "mobile-wheels" }`
460
+ ([#86](https://github.com/ElliotGarbus/kivyforge/issues/86)). The syntax
461
+ matches uv's `[[tool.uv.index]]` and `[tool.uv.sources]`. A pinned package
462
+ comes only from its index, for every package that depends on it too. An
463
+ `explicit` index serves only the packages pinned to it, so it can't replace
464
+ anything else. `pypi` is a built-in name. A non-explicit named index is an
465
+ extra index for every target, ranked after the target's `extra_index_urls`
466
+ and before PyPI.
467
+ - **Added:** a pin outranks `find_links`. To switch a package from your own
468
+ wheel to an index's release, pin it; to switch back, remove the pin. Your
469
+ wheel stays where it is, and `lock` warns (`KF-LOCK-WARNING`) that it is
470
+ ignoring it. `lock` also warns about an explicit index that no `sources`
471
+ table uses. A pinned package with no wheel for a target on its index fails
472
+ with `KF-LOCK-SOURCE-UNAVAILABLE`; kivyforge never falls back to another
473
+ index.
474
+ - **Changed:** iOS `lock` now reports warnings, like the other targets.
475
+ - **Migration:** run `kivyforge lock` once per platform. The new keys are lock
476
+ inputs, so every existing lock reads as stale until re-locked, as after
477
+ #126; the re-lock keeps every locked version. A project that sets none of
478
+ the new keys resolves exactly as before.
479
+
480
+ ### Your own wheels win, and the first-listed index wins a tie
481
+
482
+ - **Changed:** a package with any wheel in a `find_links` directory now comes
483
+ only from `find_links`, on every architecture or ABI, even when an index has
484
+ a newer version or a better-matching wheel
485
+ ([#86](https://github.com/ElliotGarbus/kivyforge/issues/86)). pip ranks
486
+ candidates by version and wheel tag, never by source, so a patched wheel you
487
+ built yourself used to lose to an index wheel of the same version.
488
+ `lock --update` moves a local package to a newer version only when a newer
489
+ wheel is in the directory. If the directory has a wheel for some ABIs but
490
+ not others, `lock` fails with the new `KF-LOCK-SOURCE-UNAVAILABLE` instead
491
+ of filling the gap from an index.
492
+ - **Changed:** when two indexes offer the same version of a package with
493
+ equally good wheels, the one listed first in `extra_index_urls` wins, and
494
+ PyPI ranks last. pip took the one listed last, which read as backwards. A
495
+ newer version or a better-matching wheel still wins whatever its index's
496
+ position.
497
+ - **Added:** each package in `pylock.<platform>.toml` records the index its
498
+ wheels came from, in PEP 751's standard `index` key, so a package that
499
+ changes index shows up in the lock diff and in `lock --check`. The unused
500
+ `[packages.tool.kivyforge] source_index` key is no longer written.
501
+ - **Changed:** desktop locks now run pip through the same shim as Android and
502
+ iOS, which applies these rules. Markers are still evaluated for the host,
503
+ as before.
504
+ - **Migration:** none needed for most projects. Existing locks stay valid:
505
+ `lock --check` does not count a missing `index` as drift, and the next
506
+ re-lock adds it. A project whose `find_links` directory holds an older
507
+ version of a package that an index also ships gets the local one at its
508
+ next re-lock; remove the wheel to keep the index version. A project with two
509
+ or more `extra_index_urls` that publish the same version of a package now
510
+ gets it from the first listed.
511
+
512
+ ### `include_files` on iOS and macOS
513
+
514
+ - **Added:** `[[tool.kivy.ios.include_files]]` and
515
+ `[[tool.kivy.macos.include_files]]` copy your own files into the app, with
516
+ the same `dest` and `sources` keys as Android
517
+ ([#170](https://github.com/ElliotGarbus/kivyforge/issues/170)). On iOS `dest`
518
+ is relative to the `.app` (`"."` is its root), which is where Firebase looks
519
+ for `GoogleService-Info.plist`. On macOS it is relative to `Contents/` and
520
+ must start with `Resources` or `SharedSupport`, the places Apple documents
521
+ for data; macOS also drops the `com.apple.quarantine` attribute a download
522
+ adds. Files are copied byte for byte, and `kivyforge lock` pins each one by
523
+ SHA-256. After building, kivyforge checks that the `.app`, the `.ipa` and a
524
+ signed macOS bundle (before it goes to the notary service) hold each file
525
+ as it was copied, and fails with the new `KF-ARTIFACT-CONTENT` (exit 5)
526
+ without reporting the product if one is missing or different. Three new
527
+ codes (exit 1) refuse an include before anything is built:
528
+ `KF-INCLUDE-RESERVED` for a path the build writes (`Info.plist`,
529
+ `Resources/app`, ...) or, on iOS, a code-bundle name such as `X.framework`;
530
+ `KF-INCLUDE-NATIVE-CODE` for a Mach-O file or static archive, which
531
+ belongs in the platform's native-code key; and `KF-INCLUDE-SOURCE-FORMAT`
532
+ for a format Xcode compiles (`.xcstrings`, `.storyboard`, `.xib`,
533
+ `.xcassets`). `doctor -p ios` and `-p macos` report include drift.
534
+ - **Migration:** `include_files` is now a lock input on iOS and macOS, so
535
+ every existing `pylock.ios.toml` and `pylock.macos.toml` reads as stale once.
536
+ Run `kivyforge lock -p ios` and `kivyforge lock -p macos`; both keep the
537
+ pinned versions.
538
+
539
+ ### Entry points: `module:function` and `[project.gui-scripts]`
540
+
541
+ - **Added:** `[tool.kivy].entry_point` may name a function, such as
542
+ `"myapp.app:main"`, and kivyforge calls it the way an installed script calls
543
+ it: it imports `myapp.app` as part of its package, calls `main()` with no
544
+ arguments, and uses the return value as the exit status
545
+ ([#68](https://github.com/ElliotGarbus/kivyforge/issues/68)). Relative
546
+ imports inside the package now work, which they cannot when the module runs
547
+ as `__main__`. If `entry_point` is unset and `[project.gui-scripts]` has
548
+ exactly one entry, kivyforge uses it, so a package that already declares
549
+ its GUI script for `pip` and `uv run` needs no `entry_point`. If there are
550
+ several entries, kivyforge asks you to choose one with `entry_point`.
551
+ `[project.scripts]` is never used. This works on Android, iOS, macOS, Linux
552
+ and Windows: kivyforge generates a small launcher that each platform runs
553
+ as `__main__`, and `strip_source` release builds compile it to `.pyc` with
554
+ the rest of the app.
555
+ - **Changed:** `kivyforge init` no longer writes `entry_point = "main"` for a
556
+ project that declares GUI scripts. That line would override the GUI
557
+ script. With one GUI script, `init` writes no `entry_point` and adds a
558
+ comment naming the script. With several, it sets `entry_point` to the
559
+ first and lists the others. A project without GUI scripts gets the same
560
+ line as before. `init --force` still leaves `[tool.kivy]` alone.
561
+ - **Migration:** none needed for an app that sets `entry_point` or declares no
562
+ GUI scripts. `entry_point = "main"` and other module names still run as
563
+ `__main__`, exactly as before. A project that declares exactly one GUI
564
+ script and relied on the default `"main"` now starts that GUI script;
565
+ set `entry_point = "main"` to keep the old behavior.
566
+
567
+ ### `-v` / `-vv`: Gradle's stack trace and detail, without leaving kivyforge
568
+
569
+ - **Added:** every verb accepts `-v` / `--verbose`, and `-vv`, or
570
+ `KIVYFORGE_VERBOSE=1|2` ([#64](https://github.com/ElliotGarbus/kivyforge/issues/64)).
571
+ On Android, `-v` adds `--stacktrace` to every Gradle run and prints the
572
+ folder and exact `gradlew` command first, so you can rerun it by hand; `-vv`
573
+ also adds `--info`. When Gradle fails, kivyforge's message now says to rerun
574
+ with `-v` (or, at `-v`, repeats the command) instead of leaving you with
575
+ Gradle's "Run with --stacktrace", which you could not pass. Other platforms
576
+ accept the flag and ignore it for now. The extra lines go to stderr, so
577
+ `--json` output is unchanged. `kivyforge capabilities` reports which verbs
578
+ accept it and which platforms act on it (`verbose` in each `verbs` and
579
+ `platforms` entry).
580
+ - **Migration:** none needed; without the flag, Gradle runs exactly as before.
581
+
582
+ ### Locks: the desktop Python runtime lookup takes seconds, not half a minute
583
+
584
+ - **Changed:** to pin the python-build-standalone runtime, `kivyforge lock`
585
+ for macOS, Linux and Windows now lists the release tags and reads each
586
+ release's small `SHA256SUMS`, eight at a time, instead of paging through
587
+ GitHub's full release records (about 15 MB a page). For CPython 3.13.14 on
588
+ Windows the lookup went from 10 to 47 seconds to 2 or 3, and it now makes
589
+ one GitHub API call instead of up to sixteen, so CI hits the rate limit far
590
+ less. The pinned URL and SHA-256 are the same as before; the old search
591
+ remains as a fallback.
592
+ - **Migration:** none needed.
593
+ ### iOS: safe-area insets and keyboard height are used as Kivy pixels
594
+
595
+ - **Changed:** the mobile geometry and kivy-ios `ios` module guides, and the
596
+ examples that pad by the safe area (`android-safe-area`, `ios-webview`,
597
+ `keychain-spm`, `mobile-geometry`, `pyjnius-deviceinfo`, `pyobjus-ball`,
598
+ `pyobjus-deviceinfo`, `qr-maven`, `svg-explorer`), no longer multiply
599
+ `kivy.mobile.get_safe_area()` or `get_keyboard_height()` by `get_scale()`
600
+ on iOS. [kivy/kivy#9441](https://github.com/kivy/kivy/pull/9441) makes the
601
+ iOS backend return Kivy layout pixels, as Android already did, which also
602
+ fixes `Window.keyboard_height`, `Window.safe_area` and `softinput_mode` on
603
+ iOS. `mobile-geometry` now labels these values `px`.
604
+ - **Migration:** this needs a Kivy build that includes kivy/kivy#9441. On an
605
+ earlier Kivy 3 build, these examples' iOS insets and keyboard heights are a
606
+ third of the right size on current iPhones. If your own app multiplies these
607
+ values by `get_scale()` on iOS, remove the multiplication when you move to
608
+ the fixed Kivy, or they are scaled twice.
609
+
610
+ ### Locks: a desktop re-lock keeps its Python runtime
611
+
612
+ - **Changed:** on macOS, Linux and Windows, `kivyforge lock` and
613
+ `lock --check` keep the python-build-standalone runtime already in the lock
614
+ while its CPython version, architectures and floor are unchanged, the way
615
+ they keep package pins. That skips the GitHub lookup for the runtime, which
616
+ took 20 to 30 seconds on Windows: a re-lock of `dice-roller` after an
617
+ input edit went from about 25 seconds to 6, and `lock --check` makes no
618
+ GitHub API call at all, so it no longer counts against CI's rate limit.
619
+ `lock --update`, a new Python version, or a new architecture looks the
620
+ runtime up again.
621
+ - **Migration:** none needed. A newer python-build-standalone build of the
622
+ same CPython version is picked up by `lock --update`.
623
+
624
+ ### Locks: desktop `lock` shows the Python runtime lookup
625
+
626
+ - **Fixed:** `kivyforge lock -p macos`, `-p linux` and `-p windows` showed
627
+ nothing while finding the python-build-standalone build of the pinned
628
+ CPython, a GitHub API lookup that pages back through releases for a version
629
+ not in the newest one. On Windows, CPython 3.13.14 took 28 seconds before
630
+ the first line. `lock` now announces the lookup and each page it checks:
631
+ `[lock] CPython 3.13.14: finding the python-build-standalone build`, then
632
+ `checking older releases (page N)`.
633
+ - **Migration:** none needed.
634
+
635
+ ### Locks: iOS and desktop `lock` show their progress
636
+
637
+ - **Added:** `kivyforge lock -p ios`, `-p macos`, `-p linux` and `-p windows`
638
+ now report each step on stderr, as `lock -p android` has since #87
639
+ ([#88](https://github.com/ElliotGarbus/kivyforge/issues/88)): a line per
640
+ slice or arch as pip resolves, a byte bar for each wheel pip downloads, a bar
641
+ for the python.org iOS framework download, the packages
642
+ `swift package resolve` fetches, and a "still resolving" line after 30
643
+ seconds with nothing new to show. In a log or a pipe, each bar becomes a few
644
+ lines. Before, these steps ran silently, so a slow lock looked like a hang.
645
+ - **Migration:** none needed. stdout, and the `--json` envelope, are unchanged.
646
+
647
+ ### iOS: a guide to replacing the kivy-ios `ios` module, and a warning
648
+
649
+ - **Added:** the guide
650
+ [Replace the kivy-ios ios module](docs/guides/guides/ios/ios-module.md).
651
+ kivyforge does not provide the module
652
+ ([#109](https://github.com/ElliotGarbus/kivyforge/issues/109)): Kivy 3 and
653
+ the standard library replace every function, on every platform. The guide
654
+ maps each old name to its replacement, explains which values are UIKit
655
+ points and which are Kivy pixels, and gives a web view recipe. It is
656
+ linked from the migration and mobile geometry guides.
657
+ - **Added:** `kivyforge build -p ios` and `kivyforge doctor -p ios` warn with
658
+ `KF-IOS-LEGACY-IOS-MODULE` when a `.py` file under `app_dir` imports `ios`,
659
+ naming the file and line and pointing at the guide. The build goes on, and
660
+ under `--json` the warning is in `diagnostics` on an `ok: true` run. It is
661
+ quiet when `app_dir` has its own `ios.py` or `ios/`. Dependencies aren't
662
+ scanned.
663
+ - **Docs:** [Call iOS APIs with pyobjus](docs/guides/guides/ios/pyobjus.md) now
664
+ says plyer's iOS facades need `pyobjus`, that `pyobjus`'s `load_framework`
665
+ does nothing on iOS, and that a `CGRect` argument should go through ctypes,
666
+ because pyobjus corrupts the heap passing one by value
667
+ ([kivy/pyobjus#90](https://github.com/kivy/pyobjus/issues/90)).
668
+ ### iOS: `run --simulator` shows the simulator with Xcode 27
669
+
670
+ - **Fixed:** with Xcode 27, `run -p ios --simulator` launched the app on a
671
+ simulator with no window. Xcode 27 replaced Simulator.app with Device Hub,
672
+ and kivyforge opened the window with `open -a Simulator`, which now fails,
673
+ and ignored the failure. kivyforge now opens the simulator's screen in
674
+ Device Hub (`devices://device/open?id=<UDID>`). With Xcode 26 and earlier it
675
+ opens the selected Xcode's own Simulator.app.
676
+ - **Changed:** the window is opened on every `run --simulator`, not only when
677
+ kivyforge boots the simulator. A simulator that was already booted with no
678
+ window now gets one.
679
+ - **Added:** if no window can be opened, `run` warns on stderr and still runs
680
+ the app.
681
+ - **Migration:** none needed.
682
+
683
+ ### iOS: Kivy 2.x is refused with a clear error
684
+
685
+ - **Added:** `kivyforge lock -p ios` fails with `KF-IOS-KIVY-UNSUPPORTED` (exit
686
+ `1`) when the `kivy` requirement admits no Kivy 3 release, such as `kivy<3`
687
+ or `kivy==2.3.1`, and when the resolved `kivy` is below `3.0.0.dev0`.
688
+ `kivyforge build -p ios` applies the second check to the lock it reads, so a
689
+ lock written by hand or by an older kivyforge gets the same error
690
+ ([#109](https://github.com/ElliotGarbus/kivyforge/issues/109)). Before, a
691
+ 2.x requirement failed inside pip's resolver with pip's wording, and
692
+ nothing said that Kivy 2.x is unsupported on iOS. kivyforge builds Kivy 3
693
+ only there; kivy-ios serves Kivy 2.x.
694
+ - **Migration:** none for a project already on Kivy 3. A project that pins
695
+ Kivy 2.x now gets this error instead of pip's: require Kivy 3 (for example
696
+ `kivy>=3.0,<4`) and run `kivyforge lock -p ios`.
697
+
698
+ ### Hosts: every target verb refuses a host that cannot build the target
699
+
700
+ - **Changed:** `lock`, `build`, `run`, `package`, `open`, `status` and `upgrade`
701
+ now all apply the host table from `kivyforge capabilities`, and fail with
702
+ `KF-HOST-INCAPABLE` (exit `3`) on the wrong host. iOS and macOS build on
703
+ macOS, Windows on Windows, Linux on Linux, and Android on any of them. Before,
704
+ only `build`, `run` and `package` checked the host, plus `lock` and `open`
705
+ for iOS. `lock -p macos` on Windows resolved a lock, and `status` and
706
+ `upgrade` worked for any target. The JSON envelope still names the refused
707
+ target in `platform`.
708
+ - **Changed:** Linux targets build only on an x86_64 Linux host. A Raspberry
709
+ Pi is a target, not a build host: add `"aarch64"` to `[tool.kivy.linux].archs`
710
+ on an x86_64 Linux machine and copy the AppImage to the Pi. kivyforge refuses
711
+ aarch64 hosts because none is in the test matrix. `capabilities --json`
712
+ publishes the rule as `host_machines` on each platform (an empty list means
713
+ any CPU).
714
+ - **Unchanged:** `init` still writes configuration for any target on any host,
715
+ so one `pyproject.toml` can describe every platform. `doctor` still runs on
716
+ the wrong host and reports why the host cannot build the target.
717
+ - **Migration:** run each target's `lock`, `status` and `upgrade` on a host that
718
+ can build that target, as CI already does.
719
+
720
+ ### Locks: re-lock only when a lock input changes, and keep existing pins
721
+
722
+ - **Changed:** a lock goes stale only when something it was resolved from
723
+ changes ([#126](https://github.com/ElliotGarbus/kivyforge/issues/126)): the
724
+ dependencies and `requires-python`, plus that platform's Python version,
725
+ architectures, OS floor, package indexes, `find_links`, `exclude` and native
726
+ libraries. Comments, key order, signing, icons, the display name, the version
727
+ and every other platform's overlay no longer stale it. Editing iOS signing
728
+ leaves `pylock.android.toml` in sync. The lock records this as
729
+ `lock_inputs_sha256`, replacing `pyproject_sha256`.
730
+ - **Changed:** `kivyforge lock` keeps the versions already in the lock. After
731
+ an input change it moves only what the change requires; a dependency whose new
732
+ specifier excludes its locked version moves, and the rest stay. If the locked
733
+ versions cannot be kept, it fails with the new `KF-LOCK-PINS-CONFLICT` (exit
734
+ `1`) and names the conflict. `kivyforge lock --update` still takes the newest
735
+ version each specifier allows.
736
+ - **Changed:** `kivyforge lock --check` resolves the same way, so a new upstream
737
+ release no longer fails CI while `pyproject.toml` is unchanged.
738
+ - **Migration:** run `kivyforge lock -p <platform>` once for each platform you
739
+ lock. Every existing lock reads as out of date, because it records the old
740
+ whole-file hash. The re-lock keeps the versions already pinned.
741
+
742
+ ### Android: emulator and adb failures say why
743
+
744
+ - **Fixed:** when `run -p android` or `run --smoke` booted an AVD and the
745
+ emulator could not start, kivyforge waited five minutes and then reported
746
+ only that the emulator "did not finish booting (serial: never appeared)".
747
+ The emulator's output now goes to a log (`<temp>/kivyforge/emulator-<avd>.log`).
748
+ If the emulator exits before its device is ready, the run fails at once and
749
+ shows the last lines of that log, for example a missing `libpulse.so.0` in
750
+ WSL. Every boot failure, including the timeout, names the log's path.
751
+ - **Added:** on Windows, `doctor -p android` warns when the adb server port
752
+ (5037) is held by a process other than `adb.exe`, names that process, and
753
+ says how to fix it. The usual cause is `wslrelay.exe`: an adb server running
754
+ inside WSL, which Windows adb then reaches and which cannot see Windows
755
+ emulators or USB devices. Before, doctor said only "no device or emulator
756
+ attached".
757
+ - **Migration:** none needed.
758
+
759
+ ### Dynamic version: a quoted pip error keeps its non-ASCII characters on Windows
760
+
761
+ - **Fixed:** when a dynamic `[project].version` could not be resolved
762
+ (`KF-VERSION-UNRESOLVED`), the pip lines kivyforge quotes were decoded with
763
+ the locale's encoding. On a cp1252 Windows host, every non-ASCII character in
764
+ them came out garbled (`café` as `café`). They are now decoded as UTF-8,
765
+ which is what pip writes to a pipe.
766
+ - **Fixed:** a dynamic version could not be resolved when the interpreter
767
+ running kivyforge was older than `[project].requires-python`, for example
768
+ an Android app declaring `>=3.14` built from a host with Python 3.13. pip
769
+ refused with "requires a different Python". `requires-python` describes the
770
+ bundled runtime, so the version lookup now ignores it.
771
+ - **Changed:** when the version lookup fails, the `Fix:` line now says that
772
+ pip reads its own index settings (`PIP_INDEX_URL`, `PIP_EXTRA_INDEX_URL`,
773
+ `pip.conf`), not `[[tool.uv.index]]` or `[[tool.kivy.index]]`. A backend on
774
+ a private index configured only for uv failed with pip's "No matching
775
+ distribution" and nothing pointing at the fix.
776
+ - **Migration:** none needed.
777
+
778
+ ### Windows: local wheels lock correctly, and an app finds its metadata once
779
+
780
+ - **Fixed:** on a Windows host, `kivyforge lock` for macOS, Linux or Windows
781
+ recorded a wheel from `find_links` as an absolute path with its drive letter
782
+ missing (`Users/you/project/wheels/...`), so the next `build` failed with
783
+ "vendored artifact not found". It now records the path relative to the
784
+ project (`wheels/...`), as iOS and Android already did.
785
+ - **Fixed:** a Windows app had its `app` folder on `sys.path` twice, so
786
+ `importlib.metadata.distributions()` returned the app's own distribution
787
+ twice. It is now on `sys.path` once, still first.
788
+ - **Migration:** on Windows, run `kivyforge lock -p <platform>` again if the
789
+ project uses `find_links`, to rewrite a lock with the drive-less path. The
790
+ launcher change takes effect on the next `build`.
791
+
792
+ ### Android: `package --debug` puts a debug build in `dist/`
793
+
794
+ - **Added:** `kivyforge package -p android --debug` packages a debug build
795
+ ([#112](https://github.com/ElliotGarbus/kivyforge/issues/112)). It builds
796
+ what `build -p android --debug` builds, needs no keystore, and copies it
797
+ into `dist/android/` with a `-debug` suffix:
798
+ - `dist/android/myapp-1.2.0-debug.apk`, signed with the Android SDK's debug
799
+ key;
800
+ - `dist/android/myapp-1.2.0-debug.aab` with `-f aab`, unsigned, as Gradle
801
+ builds a debug AAB.
802
+
803
+ The release-only checks, Lint and the manifest policy, are skipped.
804
+ `--keystore` and `--key-alias` cannot be combined with `--debug`, and
805
+ `--debug` is rejected for other platforms. `capabilities --json` reports a
806
+ new `package_debug` key for each platform.
807
+ - **Migration:** none needed. A plain `package` is unchanged.
808
+
809
+ ### Android: `doctor` no longer warns about acceleration on every Mac
810
+
811
+ - **Fixed:** `kivyforge doctor -p android` warned "AVDs present (N) but no
812
+ hardware acceleration" on every Mac, even when the emulator was accelerated,
813
+ because it looked for `/dev/kvm`, which only Linux has. On macOS it now asks
814
+ the kernel whether Hypervisor.Framework is available
815
+ (`sysctl kern.hv_support`), so the check passes on a Mac that can run an
816
+ accelerated emulator. When acceleration is missing, the hint now names your
817
+ host's own hypervisor: KVM on Linux, Hypervisor.Framework on macOS, WHPX or
818
+ AEHD on Windows. It no longer suggests HAXM, which is discontinued.
819
+ - **Migration:** none needed.
820
+
821
+ ### Android and iOS: `package` puts its artifact in `dist/`
822
+
823
+ - **Changed:** `kivyforge package -p android` and `-p ios` copy the finished
824
+ artifact into `dist/android/` and `dist/ios/`, named with the app and its
825
+ version, as `package -p linux` already does
826
+ ([#112](https://github.com/ElliotGarbus/kivyforge/issues/112)):
827
+ - `dist/android/myapp-1.2.0.apk` or `.aab`;
828
+ - `dist/ios/myapp-1.2.0.ipa`.
829
+
830
+ Two cases add to the name:
831
+ - a package that `--abi` limits to some of your configured ABIs names the
832
+ ABI (`myapp-1.2.0-x86_64.apk`), so it is never mistaken for the full app;
833
+ - an `ad-hoc` or `development` export names its method
834
+ (`myapp-1.2.0-ad-hoc.ipa`), since each embeds a different provisioning
835
+ profile.
836
+
837
+ The `Packaged` line and `data.artifacts` name the copy in `dist/`. A file of
838
+ the same name from an earlier run is kept until the new copy succeeds, and
839
+ files of other versions are left alone.
840
+ - **Migration:**
841
+ - Gradle's `app-release.apk` or `app-release.aab`, and Xcode's
842
+ `<app>-ios/build/<app>.ipa`, are still written where they were, so a
843
+ script that reads them keeps working. Read the `dist/` path, or
844
+ `data.artifacts[].path` from `--json`, instead.
845
+ - `kivyforge clean` now also removes `dist/android/` and `dist/ios/`, with
846
+ every version in them.
847
+ - `build` is unchanged, including `build -p android --debug` and
848
+ `build -p ios --release`.
849
+ - If the same checkout also uploads a wheel, `twine upload dist/*` fails on
850
+ the platform folders, as it already does on `dist/linux/` and
851
+ `dist/windows/`. See
852
+ [Drive kivyforge from CI](docs/guides/guides/cross-platform/ci-and-agents.md#upload-python-distributions-from-a-separate-folder).
853
+
854
+ ### iOS: `doctor` checks the Team ID
855
+
856
+ - **Added:** `kivyforge doctor -p ios` has a "Team ID" check
857
+ ([#108](https://github.com/ElliotGarbus/kivyforge/issues/108)). It reads the
858
+ Team ID that `--device` and `--release` builds sign with, from
859
+ `KIVYFORGE_TEAM_ID` or `[tool.kivy.ios.signing].team_id`, and compares it
860
+ with the teams of the signing certificates in your keychain. The check:
861
+ - fails when the value is not 10 capital letters and digits;
862
+ - fails when the value is the ID in parentheses in an `Apple Development`
863
+ certificate's name, which is not a Team ID but is often pasted as one,
864
+ and names that certificate's Team ID instead;
865
+ - fails when a pinned `provisioning_profile` is for a different team;
866
+ - warns when no certificate in your keychain is for that team, because
867
+ automatic signing can still create one during the build.
868
+
869
+ Before, a wrong Team ID failed only at the end of the build, inside
870
+ xcodebuild. With no Team ID set, the check is skipped, since simulator builds
871
+ don't sign, and it lists the Team IDs in your keychain, each with its team's
872
+ name, so you can copy one. See
873
+ [Find your Team ID](docs/guides/guides/ios/signing.md#find-your-team-id).
874
+ - **Migration:** none needed. `doctor` now fails a project whose Team ID would
875
+ have failed the build; `build` itself is unchanged.
876
+
877
+ ### Windows and Linux: a failed write into `dist/` is reported, not a traceback
878
+
879
+ - **Fixed:** when `kivyforge package -p windows` could not copy the bundle into
880
+ `dist/windows/`, or `kivyforge package -p linux` could not write the
881
+ `.AppImage` into `dist/linux/`, the filesystem error escaped as a Python
882
+ traceback, and `--json` printed no envelope. It is now an ordinary failure:
883
+ `Error: ...` on stderr, and with `--json` one envelope with `ok: false`, code
884
+ `KF-ERROR` and exit status 1. Your previous package is still restored. A
885
+ common cause on Windows is a path too long for Windows with long paths off,
886
+ the default: the `dist\windows\<Name>-<version>-<arch>\` folder is longer
887
+ than `build\windows\<Name>\`, so a bundle that builds can still fail to
888
+ copy. The message names the first path that failed and how many failed, and
889
+ when that path is too long it suggests a shorter project folder.
890
+ - **Migration:** none needed.
891
+
892
+ ### All platforms: a failed write into the build folder is reported, not a traceback
893
+
894
+ - **Fixed:** when `kivyforge build` or `kivyforge package` could not write
895
+ into the folder it assembles the app in (`build/macos/`, `build/windows/`,
896
+ `build/linux/`, `<app>-ios/` or `<app>-android/`), the filesystem error
897
+ escaped as a Python traceback, and `--json` printed no envelope. A full disk,
898
+ a read-only folder, or a file where a folder belongs all did this. It is now
899
+ an ordinary failure: `Error: could not ...` on stderr, and with `--json` one
900
+ envelope with `ok: false`, code `KF-ERROR` and exit status 1. As for `dist/`,
901
+ a copy that fails for many files names the first and counts the rest, and on
902
+ Windows a path too long with long paths off gets the same advice. This also
903
+ covers the copy of `appimagetool` that `package -p linux` runs from the
904
+ artifact cache, and `ExportOptions.plist` in an iOS release build. When an
905
+ iOS build finds a file where the `<app>-ios/app` folder belongs, its "remove
906
+ it and re-run build" message is now an error, not the last line of a
907
+ traceback.
908
+ - **Migration:** none needed.
909
+
910
+ ### macOS and Linux: a build that fails at the last step keeps your previous app
911
+
912
+ - **Fixed:** `build`, `package` and `run` for macOS and Linux assemble the new
913
+ `.app` or AppDir in a hidden temporary folder in `build/macos/` or
914
+ `build/linux/`, then swap it in for the previous one. The swap deleted the
915
+ previous `.app` or AppDir first. If that delete failed partway, for example
916
+ on a folder kivyforge may not write, or the new folder could not take its
917
+ place, the build failed with your previous app half deleted and the
918
+ temporary `.<Name>.app.tmp-*` or `.<Name>.AppDir.tmp-*` folder left behind.
919
+ Now the previous app is renamed aside whole, the new one is renamed into
920
+ place, and only then is the previous one deleted. If the new one cannot take
921
+ its place, the previous one is put back, and the temporary folder is always
922
+ removed. The failure is reported as before, as `KF-ERROR` with exit
923
+ status 1. If the new app is in place but part of the previous one cannot be
924
+ deleted, the build still succeeds and leaves a hidden
925
+ `.<Name>.app.old-*` or `.<Name>.AppDir.old-*` folder you can delete.
926
+ - **Migration:** none needed. A `.<Name>.app.tmp-*` or `.<Name>.AppDir.tmp-*`
927
+ folder left by an earlier failed build is safe to delete.
928
+
929
+ ### Guides
930
+
931
+ - **Added:** [Sign your iOS app](docs/guides/guides/ios/signing.md#find-your-team-id)
932
+ explains how to find your Team ID
933
+ ([#108](https://github.com/ElliotGarbus/kivyforge/issues/108)). With a paid
934
+ membership it is on the Apple Developer website. A free Apple ID's personal
935
+ team has no page there, so the guide shows how to read the ID from the
936
+ team's `Apple Development` certificate in Keychain Access, and warns that
937
+ the ID in parentheses in the certificate's name is a different value.
938
+
939
+ ## [v3.0.0.dev1](https://github.com/ElliotGarbus/kivyforge/tree/v3.0.0.dev1) - 2026-10-14
940
+
941
+ ### All platforms: the app's own package metadata
942
+
943
+ - **Added:** kivyforge writes each app's package metadata into the app, from
944
+ `[project]` ([#122](https://github.com/ElliotGarbus/kivyforge/issues/122)):
945
+ a `<name>-<version>.dist-info` folder beside the app's code on macOS, Linux,
946
+ Windows, Android and iOS. Inside the app, `importlib.metadata` now finds the
947
+ app's name, its version (the same value as `versionName` and
948
+ `CFBundleShortVersionString`), the entry points of `[project.scripts]`,
949
+ `[project.gui-scripts]` and `[project.entry-points]`, and its modules
950
+ through `packages_distributions()`, for every build backend and layout, with
951
+ or without a development install. Before, an app had metadata only if a
952
+ setuptools dev install had left an `egg-info` in `app_dir`, as old as that
953
+ install. See
954
+ [Read your package metadata inside the app](docs/guides/guides/cross-platform/versioning.md#read-your-package-metadata-inside-the-app).
955
+ - **Changed:** a development `*.egg-info` folder at the top of `app_dir`, and a
956
+ `.dist-info` folder of the app's own name there, no longer ship in the app;
957
+ the generated folder replaces them. Your source tree is not changed. On iOS,
958
+ `kivyforge build` stages the folder in `<app>-ios/_kivyforge_meta/` and the
959
+ generated Build Python script copies it into the bundle, so Xcode-only
960
+ rebuilds keep it. **Migration:** an app that read its metadata from the
961
+ `egg-info` now reads the same names from the generated folder, current with
962
+ the build; nothing to change. An app whose code looks for the `egg-info`
963
+ folder itself should use `importlib.metadata` instead.
964
+ - **Changed:** a setuptools project that declares entry points only in
965
+ `setup.cfg` (`[options.entry_points]`) or `setup.py` (`entry_points=`), and
966
+ none in `[project]`, fails `build`, `package` and `run` with
967
+ `KF-METADATA-ENTRY-POINTS-OUTSIDE-PROJECT` (exit 1). kivyforge writes the
968
+ metadata from `[project]`, so the app would otherwise start without them.
969
+ **Migration:** move them into `[project.entry-points]`, console scripts into
970
+ `[project.scripts]` and GUI scripts into `[project.gui-scripts]`. setuptools
971
+ has deprecated reading them from those files for a project with a
972
+ `[project]` table.
973
+ - **Changed:** `[project]` entry points and `[project].name` are checked as
974
+ build backends check them, and each violation is a config error at its line
975
+ in every verb: a `console_scripts` or `gui_scripts` group under
976
+ `[project.entry-points]`, a nested table (usually a dotted group name left
977
+ unquoted), an invalid group name, an entry point name with `=` or a leading
978
+ `[` or whitespace, and a project name that is not valid. **Migration:** pip
979
+ could not have built such a project either; fix the table the message names.
980
+ - **Added:** when `[project].dynamic` lists `entry-points`, `scripts` or
981
+ `gui-scripts`, kivyforge cannot know the entry points, so it generates
982
+ nothing, leaves `app_dir` as it is, and warns with
983
+ `KF-METADATA-NOT-GENERATED`. Its `context` names the `reason` and the
984
+ development `egg-info` that ships instead (`fallback`).
985
+ - **Added:** Android's contract self-test, run by `run --smoke`, checks that
986
+ the app finds the metadata kivyforge generated.
987
+
988
+ ### iOS: rebuilding in Xcode keeps a device build signed
989
+
990
+ - **Fixed:** rebuilding a device build in Xcode (Cmd-B or Cmd-R, or re-running
991
+ the `xcodebuild` command kivyforge ran) succeeded but left the `.app` with an
992
+ invalid signature: `codesign --verify --deep --strict` reported "a sealed
993
+ resource is missing or invalid" and named every
994
+ `Frameworks/<module>.framework` binary. The Build Python script re-signs
995
+ those frameworks on every run, and with a real signing identity that changes
996
+ their bytes. Xcode didn't know the script writes into the bundle, so it
997
+ didn't sign the app again, and the app's signature still described the old
998
+ bytes. The script now declares what it writes (`python/`, `pip-deps/` and
999
+ `Frameworks/`) as its outputs, and Xcode signs the app again after it. That
1000
+ adds about a second to an incremental build. The app from such a build still
1001
+ installed and ran on iOS 27, but anything that checks its signature rejects
1002
+ it. Simulator builds, signed ad hoc, stayed valid. A project generated by an
1003
+ earlier kivyforge gets the fix at its next `kivyforge build`, which
1004
+ regenerates the script phase.
1005
+
1006
+ ### iOS: the default deployment target is 16.0
1007
+
1008
+ - **Fixed:** with Xcode 27, `kivyforge build -p ios` failed for any project
1009
+ that did not set `[tool.kivy.ios].deployment_target`. The default was 13.0,
1010
+ and Xcode 27 accepts only 15.0 to 27.0.x, so xcodebuild stopped at the end
1011
+ of the build ("The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set
1012
+ to 13.0, but the range of supported deployment target versions is 15.0 to
1013
+ 27.0.x"; `KF-BUILD-TOOL-FAILED`, exit 5). The default is now 16.0, not
1014
+ Xcode's 15.0, because that is the oldest iOS Kivy's own iOS wheels run on:
1015
+ their extension modules are built for iOS 16.0, so a lower target lets older
1016
+ devices install an app that cannot load Kivy. `kivyforge init` writes 16.0,
1017
+ and the `hello-world` example, the one example still at 13.0, now uses 16.0.
1018
+ - **Fixed:** the generated Xcode project always had 13.0 as its project-level
1019
+ `IPHONEOS_DEPLOYMENT_TARGET`, whatever `deployment_target` said. Only the app
1020
+ target had the configured value. Both levels now use it, and the next build
1021
+ of an existing `<app>-ios/` project corrects the old value.
1022
+ - **Added:** `kivyforge doctor -p ios` has a "Deployment target" check. It reads
1023
+ the range the selected Xcode accepts from the iOS SDK and fails when
1024
+ `deployment_target` is outside it, before a build gets that far. Without a
1025
+ project, it reports the range.
1026
+ - **Migration:** what to do depends on how your project sets the key.
1027
+ - **Set below 15.0:** Xcode 27 refuses the value whatever kivyforge's
1028
+ default, so raise it (to `"16.0"` for a Kivy app). Any edit to
1029
+ `pyproject.toml` makes `build` stop with `KF-LOCK-DRIFT` (exit 4), so run
1030
+ `kivyforge lock -p ios` after the edit.
1031
+ - **Not set:** you need no edit, but your app's minimum iOS rises from 13.0
1032
+ to 16.0 (`MinimumOSVersion` and `IPHONEOS_DEPLOYMENT_TARGET`), so devices
1033
+ below iOS 16 cannot install new builds. If your dependencies run on iOS 15
1034
+ (Kivy's do not), set `deployment_target = "15.0"`, the lowest value
1035
+ Xcode 27 accepts.
1036
+ - **Existing locks stay valid.** The lock checks a hash of the text of
1037
+ `pyproject.toml`, and a default is not in that text, so `build`,
1038
+ `package` and `lock --check` accept a lock made before this change. A
1039
+ re-lock does ask pip for `ios_16_0_*` tags instead of `ios_13_0_*`. Wheels
1040
+ built for an older iOS still match, so the kivy and pyobjus wheels (tagged
1041
+ `ios_13_0`) resolve to the same files. A package that publishes wheels for
1042
+ several minimum iOS versions may resolve to a different file, which makes
1043
+ `lock --check` report the lock as stale; run
1044
+ `kivyforge lock -p ios --update`.
1045
+
1046
+ ### Guides
1047
+
1048
+ - **Added:** [Manage dependencies and re-lock](docs/guides/guides/cross-platform/dependencies.md)
1049
+ explains how to pin a package with an exact specifier
1050
+ ([#107](https://github.com/ElliotGarbus/kivyforge/issues/107)). A re-lock,
1051
+ including one forced by an unrelated edit to `pyproject.toml`, resolves every
1052
+ specifier to its newest match. `lock --check` does that resolve even when the
1053
+ file hash still matches, so a range fails the check when a newer release
1054
+ appears. `build` and `package` keep accepting that lock until the hash
1055
+ changes.
1056
+ - **Fixed:** [Install kivyforge](docs/guides/get-started/install.md) now
1057
+ explains that every release so far is a pre-release, and its pipx and pip
1058
+ commands carry the pre-release flag. Without it, both fail to find kivyforge.
1059
+
1060
+ ### Android
1061
+
1062
+ - **Added:** `kivyforge lock -p android` shows a progress bar while it
1063
+ downloads the python.org CPython runtime, one per ABI, the first time that
1064
+ tarball is not already cached ([#84](https://github.com/ElliotGarbus/kivyforge/issues/84)).
1065
+ Before, that download printed nothing, so a first lock looked stuck until
1066
+ `[lock] <abi>: resolving wheels`.
1067
+ - **Changed:** `kivyforge build -p android` without `--debug` now ends with a
1068
+ line saying Gradle did not run and naming the commands that produce an APK
1069
+ ([#106](https://github.com/ElliotGarbus/kivyforge/issues/106)). Before, it
1070
+ ended on `Generated <app>-android` with exit 0 and no APK, which read like a
1071
+ successful build. What a bare build does is unchanged: it generates the
1072
+ project and stages the release payload for `package`. If you run
1073
+ `./gradlew assembleDebug` yourself on that project, the debug APK you get
1074
+ contains the release payload. Use `build --debug` instead. `--json` output
1075
+ is unchanged.
1076
+
1077
+ ### All platforms: dynamic versions
1078
+
1079
+ - **Added:** `dynamic = ["version"]` in `[project]` is supported
1080
+ ([#111](https://github.com/ElliotGarbus/kivyforge/issues/111)). A project
1081
+ that takes its version from git through setuptools-scm, hatch-vcs or another
1082
+ build backend plugin no longer fails with "missing or empty
1083
+ [project].version". `build`, `package` and `run` ask pip to prepare the
1084
+ project's metadata, in an isolated environment, and print the version they
1085
+ got. This works whether kivyforge was installed with `uv tool`, pipx or as a
1086
+ project dependency. `lock`, `doctor`, `status` and `clean` never run pip, and
1087
+ a new commit doesn't make the lock out of date. The first build needs the
1088
+ network to fetch the backend. See
1089
+ [Set the app version](docs/guides/guides/cross-platform/versioning.md).
1090
+ - **Added:** `--app-version VERSION` on `build`, `package` and `run`, and the
1091
+ `KIVYFORGE_APP_VERSION` environment variable (the flag wins), to set the
1092
+ version without running the build backend. `--no-build-isolation` resolves a
1093
+ dynamic version with a backend installed alongside kivyforge, offline.
1094
+ - **Added:** `--json` envelopes from `build` and `package` carry
1095
+ `data.version`: `{"value", "source"}`, where `source` is `override`, `static`
1096
+ or `backend`. It is recorded before the build starts, so failed builds keep
1097
+ it.
1098
+ - **Added:** `doctor` reports a Version check. New codes:
1099
+ `KF-VERSION-UNRESOLVED` (exit 1), `KF-VERSION-APPLE-FORMAT`,
1100
+ `KF-VERSION-FILE-NOT-REFRESHED`, `KF-VERSION-ISOLATION-UNUSED`.
1101
+ - **Changed:** an iOS export for the App Store (`package -p ios`, or
1102
+ `build -p ios --release`, both with the default `--export-method app-store`)
1103
+ fails before Xcode runs when the version isn't one to three dot-separated
1104
+ numbers, such as `1.0rc1` or `1.2.3.4`. App Store Connect refuses such a
1105
+ version at upload (ITMS-90060). Other iOS exports and macOS `package` warn
1106
+ instead, and debug builds are unaffected. **Migration:** a static
1107
+ pre-release version used to fail at upload; it now fails at `package`. Build
1108
+ from a release version, pass `--app-version`, or export with
1109
+ `--export-method ad-hoc` or `development`.
1110
+ - **Changed:** `[project].version` must follow PEP 440, and kivyforge writes
1111
+ its normalized form. **Migration:** `v1.2` becomes `1.2` in `versionName`,
1112
+ the plists and file names; `1.0-beta` becomes `1.0b0`; a local segment is
1113
+ lowercased with `-` turned into `.` (`1.0+Build-5` becomes `1.0+build.5`). A
1114
+ version PEP 440 can't parse, such as `1.0.0-SNAPSHOT`, now fails to load;
1115
+ pip already refused to build such a project.
1116
+ - **Changed:** a `+` in the version becomes `_` in Linux `.AppImage` and
1117
+ Windows `dist` folder names, since `+` turns into a space in URL query
1118
+ strings. **Migration:** `1.0+local` now packages as `...-1.0_local-...`.
1119
+ - **Changed:** listing `name`, `dependencies`, `authors`, `description` or
1120
+ `requires-python` in `[project].dynamic` is now an error. kivyforge reads
1121
+ those fields itself, and a dynamic one used to load as absent: a
1122
+ `dynamic = ["dependencies"]` project locked no dependencies at all.
1123
+ **Migration:** set the field statically.
1124
+
1125
+ ### Android: pyjnius versions
1126
+
1127
+ - **Fixed:** pyjnius 1.9.0 failed every Android build with "locked pyjnius 1.9.0
1128
+ is outside the bootstrap template's compatible range"
1129
+ ([#113](https://github.com/ElliotGarbus/kivyforge/issues/113)), though its
1130
+ `invoke0` method is unchanged. kivyforge no longer caps the pyjnius version.
1131
+ Instead of guessing from the version number, `build` now checks the installed
1132
+ pyjnius wheel for the `invoke0` signature kivyforge's Java glue declares, and
1133
+ fails with a message naming the wheel if it differs. New pyjnius releases that
1134
+ keep the signature work without a kivyforge update.
1135
+ - **Changed:** `kivyforge lock -p android` adds `pyjnius>=1.7.0` instead of
1136
+ `pyjnius>=1.7.0,<1.9` when your project doesn't declare it, so a re-lock picks
1137
+ the newest release. Existing locks need no change: a lock that already holds
1138
+ pyjnius 1.9.0 now builds. Older than 1.7 still fails, and `doctor` only
1139
+ checks that floor, since the wheel check needs the installed wheel.
1140
+
1141
+ ### Android: wheels built by cibuildwheel
1142
+
1143
+ - **Fixed:** numpy, pandas, matplotlib, opencv-python and other wheels that
1144
+ cibuildwheel's default repair (auditwheel) produces failed to import on the
1145
+ device with `dlopen failed: library "libc++_shared-d523468d.so" not found`.
1146
+ These wheels carry their shared libraries in a top-level `<dist>.libs/`
1147
+ directory (`numpy.libs/`), which kivyforge staged as Python extension modules
1148
+ under new names. They are now staged under their own names, like the Kivy
1149
+ wheel's `.libs/`.
1150
+ - **Fixed:** once staged, the same libraries were corrupted by the strip during
1151
+ packaging, and numpy then failed with errors like `cannot find "operty_get"
1152
+ from verneed[0]`. patchelf, which auditwheel uses, lays these libraries out in
1153
+ a way the NDK's `llvm-strip` mishandles. kivyforge now recognises them and
1154
+ keeps their symbols, and the build says how many it kept.
1155
+ - **Added:** after Gradle builds an APK or AAB, kivyforge checks every native
1156
+ library in it and fails with `KF-ANDROID-NATIVE-LIB-MISALIGNED` if one could
1157
+ not be loaded on a device, instead of shipping an app that crashes at import.
1158
+ - **Changed:** debug builds no longer strip native libraries. The default
1159
+ `strip_native_libs = "release"` was meant to keep symbols in debug builds, as
1160
+ documented, but the generated project stripped both. Debug APKs grow by the
1161
+ size of the symbols; set `strip_native_libs = true` to strip debug builds as
1162
+ before.
1163
+ - **Changed:** a wheel whose libraries directory holds a `.so` not named
1164
+ `lib*.so` (for example `libgfortran-040039e1.so.5`) now fails the build with a
1165
+ message naming the file. Android only packages `lib*.so` files, so before this
1166
+ change the library was missing from the app.
1167
+ - **Changed:** wheels from Flet's index (and others using Chaquopy's separate
1168
+ library wheels, such as `flet-libjpeg`) now fail the build with
1169
+ `KF-ANDROID-WHEEL-LAYOUT`, naming the wheels. Their libraries live under
1170
+ `opt/lib`, which kivyforge cannot load from. Before, the build failed with
1171
+ a misleading "ABI content skew" on their `.a` files, or, without those,
1172
+ built an app that could not find the libraries.
1173
+ - **Added:** a native library built for another architecture than its
1174
+ wheel's tag (an x86_64 binary in an `arm64_v8a` wheel) now fails the build
1175
+ with `KF-ANDROID-WHEEL-ARCH`, instead of failing to load on the device.
1176
+ - **Fixed:** `run -p android --release` without a configured release signing
1177
+ identity failed with "Gradle reported success for assembleRelease but no
1178
+ artifact is at ...". It said it was signing with the debug keystore, but left
1179
+ the APK unsigned; now it signs with the debug keystore.
1180
+
1181
+ - **Fixed:** libraries a package carries inside its own directory, such as
1182
+ `ffmpeg/.libs/libavcodec.so` or `blosc2/lib/libtcc.so`, were staged as
1183
+ Python extension modules under new names, so nothing could find them. They
1184
+ are now staged under their own names, like a top-level `.libs/`.
1185
+ - **Changed:** an extension module named with a suffix CPython on Android does
1186
+ not import (a build host's `.cpython-314-x86_64-linux-gnu.so`, or another
1187
+ Python version's) now fails the build with `KF-ANDROID-WHEEL-SUFFIX`.
1188
+ kivyforge's loader ignored the suffix, so such a wheel used to load under
1189
+ kivyforge while being unusable anywhere else.
1190
+
1191
+ Each auditwheel-repaired wheel carries its own renamed copy
1192
+ of the C++ runtime, so an app can load several. That is safe unless C++ objects
1193
+ pass between libraries from different wheels; see
1194
+ `docs/design/platforms/android/03-artifact-distribution-android.md`.
1195
+
1196
+ ### Wheels from more package indexes
1197
+
1198
+ - **Fixed:** wheels hosted on Cloudflare R2, such as the Android Pillow wheels
1199
+ on the Kivy School index, locked fine and then failed to download at build
1200
+ time with HTTP 403 ([#97](https://github.com/ElliotGarbus/kivyforge/issues/97)).
1201
+ The host refuses Python's default `Python-urllib` User-Agent. Every request
1202
+ kivyforge makes now identifies itself as `kivyforge/<version>`.
1203
+ - **Changed:** when an index publishes no SHA-256 for a wheel, `lock` now says
1204
+ so and gives the two fixes: use an index that publishes hashes, or put the
1205
+ wheels in a `find_links` directory, where kivyforge hashes them itself. It
1206
+ used to suggest re-running with network access, which never helps. The
1207
+ requirement itself is unchanged: kivyforge still pins every wheel by
1208
+ SHA-256. This only happens with indexes that serve wheel metadata (PEP 658)
1209
+ but no hashes, because pip then resolves without downloading the wheel.
1210
+
1211
+ ### `build` shows its downloads
1212
+
1213
+ - **Added:** on every platform, `build` (and `run`'s implicit build) now shows a
1214
+ progress bar for each Python runtime and wheel it downloads, or a few
1215
+ percentage lines when the output is a log. These downloads happen on a first
1216
+ build, after `clean --cache`, or with `--no-cache`, and used to be silent.
1217
+ - **Changed:** `clean --cache`'s help and the guides now say that pip's own cache,
1218
+ which `lock` resolves through, is left alone (`pip cache purge` clears it).
1219
+ After `clean --cache`, `lock` usually stays fast and shows no downloads.
1220
+
1221
+ ### Android: `pyjnius` is included automatically
1222
+
1223
+ - **Fixed:** an Android app whose dependencies didn't list `pyjnius` built
1224
+ without complaint and then crashed at launch with `No module named 'jnius'`
1225
+ ([#65](https://github.com/ElliotGarbus/kivyforge/issues/65)). Kivy imports
1226
+ pyjnius on Android (to read the screen density, for example) but doesn't
1227
+ declare it, because python-for-android always supplied it. Now
1228
+ `kivyforge lock -p android` adds it, in the range kivyforge supports
1229
+ (`>=1.7.0,<1.9`), when your project doesn't declare it. It is added to the
1230
+ Android lock only, so desktop installs are unaffected and you don't need a
1231
+ `; sys_platform == 'android'` marker. Declaring it yourself still works and
1232
+ chooses the version.
1233
+ - **Fixed:** a `pyjnius` requirement whose marker leaves Android out (for
1234
+ example `pyjnius; sys_platform == 'win32'`) counted as declared, so nothing
1235
+ was added, pip skipped it for Android, and `build` stopped with
1236
+ `KF-LOCK-INCOMPLETE`, which re-locking did not cure. A declaration now counts
1237
+ only if its marker holds on every ABI being locked; otherwise kivyforge adds
1238
+ its own requirement as well.
1239
+ - **Migration:** a lock made before this change, without pyjnius, now stops the
1240
+ build with `KF-LOCK-INCOMPLETE` (exit 4) instead of producing an app that
1241
+ crashes. Run `kivyforge lock -p android --update`. Projects that already
1242
+ declare pyjnius need no change.
1243
+
1244
+ ### Android: `lock` and `build` show their downloads
1245
+
1246
+ - **Fixed:** `kivyforge lock -p android` printed nothing until it finished, so a
1247
+ slow connection looked exactly like a hang
1248
+ ([#84](https://github.com/ElliotGarbus/kivyforge/issues/84)). It now reports
1249
+ each step, shows each wheel download as a progress bar (or as a few percentage
1250
+ lines when the output is a log rather than a terminal), and says it is still
1251
+ working after 30 s with nothing else to show. The Gradle step for Maven
1252
+ dependencies is announced too.
1253
+ - **Added:** before Gradle runs, a missing NDK or CMake at the versions the
1254
+ generated build pins is installed with the SDK's `sdkmanager`, with progress
1255
+ ([#74](https://github.com/ElliotGarbus/kivyforge/issues/74)). Gradle used to
1256
+ download the NDK itself in the middle of the build, a download of several
1257
+ hundred MB with no progress shown. Without `sdkmanager`, or with its license
1258
+ not accepted, `build` says so and Gradle installs it as before.
1259
+ - **Changed:** download progress is drawn by the console layer with Rich, so it
1260
+ is a live bar on a terminal and plain lines in CI logs, redirected output and
1261
+ under `--no-color`. Progress stays on stderr, so `--json` output is unaffected.
1262
+
1263
+ ### Android: adding your own resources with `include_files`
1264
+
1265
+ - **Added:** a guide section,
1266
+ [Add Android resources](docs/guides/guides/android/configure.md#add-android-resources):
1267
+ stage a folder laid out like an Android `res` folder with
1268
+ `dest = "app/src/main/res"`, keeping subfolders and qualifiers.
1269
+ ([#71](https://github.com/ElliotGarbus/kivyforge/issues/71))
1270
+ - **Added:** a file that replaces one kivyforge generates, such as
1271
+ `res/values/styles.xml` or the launcher icon, is now reported
1272
+ (`[include] ... replaces the file kivyforge generated`, and
1273
+ `KF-ANDROID-INCLUDE-OVERRIDE` under `--json`). It used to be silent.
1274
+ - **Added:** the build stops, saying what to fix, when an included
1275
+ `strings.xml` or `styles.xml` leaves `res/values/` without `app_name` or
1276
+ `Theme.Kivyforge`, which the generated manifest uses. That used to fail
1277
+ later in Gradle with an AAPT error that did not mention `include_files`.
1278
+ - **Fixed:** a file removed from `include_files`, or from a directory it
1279
+ names, is now removed from the generated project on the next build. It used
1280
+ to stay, and ship, until `kivyforge clean`.
1281
+
1282
+ ### Android: `run` follows the app's log
1283
+
1284
+ - **Changed:** `kivyforge run -p android` now shows the app's log live, the way
1285
+ pidcat or Briefcase does, until the app exits or you press Ctrl+C (which stops
1286
+ the log and leaves the app running). It used to print one snapshot 25 seconds
1287
+ after launch and return, so anything the app logged later, such as a
1288
+ traceback from a button press, never appeared, and a slow first start could
1289
+ show no Kivy output at all.
1290
+ ([#73](https://github.com/ElliotGarbus/kivyforge/issues/73))
1291
+ - **Fixed:** the log is now the app's own process (`adb logcat --pid`). The old
1292
+ filter matched text in each line, so it let other apps' lines through (any
1293
+ line mentioning `SDL` or `kivyforge`) and dropped the app's own Android crash
1294
+ report.
1295
+ - **Added:** if the app never starts, `run` prints the device log since launch
1296
+ and fails, instead of showing an empty capture.
1297
+ - **Migration:** a script that runs `kivyforge run -p android` and expects it
1298
+ to return must add `--no-follow`, which keeps the one-snapshot behaviour
1299
+ (now filtered to the app's process). `run --smoke` is unchanged.
1300
+
1301
+ ### Android: switching `kivy_generation` no longer needs `kivyforge clean`
1302
+
1303
+ - **Fixed:** rebuilding after changing `[tool.kivy.android].kivy_generation`
1304
+ failed to compile until `kivyforge clean` was run. Going from 3 to 2 failed
1305
+ with `duplicate class: org.libsdl.app.SDLInputConnection`, because the SDL3
1306
+ Java files were left in the generated project. Going from 2 to 3 failed with
1307
+ `cannot find symbol: SDLInputConnection`, because Gradle's incremental
1308
+ compiler loses a class that SDL3 moved into its own file. The build now
1309
+ removes the other generation's SDL files, and on a generation change it
1310
+ discards `app/build` (stopping the project's Gradle daemon first), so that
1311
+ one build compiles from scratch. Builds that keep the same generation are
1312
+ unaffected. ([#66](https://github.com/ElliotGarbus/kivyforge/issues/66))
1313
+
1314
+ ### Android: apps relaunch on Android 7, and services can use pyjnius below Android 12
1315
+
1316
+ - **Fixed:** on Android 7.0 and 7.1 (API 24 and 25), every app crashed on its
1317
+ second launch, and every service crashed on start, with
1318
+ `NoSuchMethodError: ... toPath()`. The check for an already-unpacked bundle
1319
+ used `java.nio.file`, which Android added in 8.0. Only the first launch after
1320
+ an install got through.
1321
+ - **Fixed:** in a service on Android 7 to 11 (API 24 to 30), `import jnius`
1322
+ raised `RuntimeError: pyjnius (Android) could not obtain a JNIEnv`. A service
1323
+ now loads the app's SDL library before Python starts, which is how pyjnius
1324
+ finds the Java VM. It creates no window.
1325
+ - **Fixed:** on Windows, `kivyforge run -p android` could crash with
1326
+ `AttributeError: 'NoneType' object has no attribute 'splitlines'` after
1327
+ launching the app. adb output is now read as UTF-8.
1328
+ - **Added:** `kivyforge run --smoke` also checks a relaunch: it unpacks the
1329
+ bundle a second time and expects the already-unpacked bundle to be used. A
1330
+ fresh install never takes that path, which is how the Android 7 crash above
1331
+ went unnoticed.
1332
+
1333
+ ### Android: the `android` package is bundled
1334
+
1335
+ - **Added:** every Android build includes python-for-android's `android`
1336
+ package, so code that imports it runs unchanged: `android.permissions`,
1337
+ `android.activity`, `android.runnable`, `android.broadcast`,
1338
+ `android.darkmode`, `android.storage`, `android.config`, and the package's
1339
+ own `mActivity`, `api_version`, `open_url` and the rest. The modules are
1340
+ taken unchanged from python-for-android at a pinned revision. plyer's
1341
+ Android facades, which import it, can now load. Nothing to add to your
1342
+ dependencies, but the package needs `pyjnius`. See
1343
+ [Use the android package](docs/guides/guides/android/android-package.md).
1344
+ - **Fixed:** on Kivy 2.3.1, tapping a `TextInput` quit the app, and so did the
1345
+ back key, `App.stop()` and `App.pause()`: Kivy calls the activity through
1346
+ this package. Kivy's Android clipboard provider now loads, and so does its
1347
+ Android audio provider when an app selects it with `KIVY_AUDIO`. A
1348
+ `TextInput` with `input_type = "number"` (or `"mail"`, `"tel"`, ...) gets
1349
+ the matching keyboard.
1350
+ - **Changed:** `kivyforge lock` fails with `KF-ANDROID-DEPENDENCY-CONFLICT`
1351
+ when a dependency is named `android`, and `kivyforge build` fails with
1352
+ `KF-ANDROID-APP-CONFLICT` when `app_dir` has an `android.py` or an `android/`
1353
+ package (or with `KF-ANDROID-DEPENDENCY-CONFLICT` when an installed wheel
1354
+ provides one). Both exit 1. The bundled package always comes first, so a
1355
+ second one would ship and never be imported.
1356
+ - **Migration:** remove `android` from your dependencies, for example a
1357
+ package index's `android` wheel or an `android` carried over from a
1358
+ buildozer `requirements` line. Rename an `android.py` or `android/` of your
1359
+ own.
1360
+
1361
+ ### Android: services have `start`, `stop` and `mService`
1362
+
1363
+ - **Added:** each generated service class has static `start(context, argument)`
1364
+ and `stop(context)` methods. A foreground service also gets the five-argument
1365
+ `start(context, icon, title, text, argument)`, which replaces the configured
1366
+ notification text for that run, and is started with
1367
+ `startForegroundService`. Inside a service,
1368
+ `autoclass("org.kivy.android.PythonService").mService` is the running
1369
+ service. These match python-for-android's services, so ported code only
1370
+ changes the class name to `org.kivy.android.Service<name>`. See
1371
+ [Add a background service](docs/guides/guides/android/services.md) and the new
1372
+ `examples/mobile/android-services`.
1373
+ - **Changed:** stopping a service now ends its process. Before, the service
1374
+ stopped but its Python code kept running in the background.
1375
+ - **Migration:** none for the intent-based start, which still works. A service
1376
+ that relied on running on after it was stopped has to stay started instead.
1377
+ pyjnius can reach the VM in a service process only on Android 12 (API 31)
1378
+ and later.
1379
+
1380
+ ### Android: a theme switch no longer quits the app
1381
+
1382
+ - **Fixed:** switching the phone between light and dark mode quit a kivyforge
1383
+ Android app, on Kivy 2.3.1 (SDL2) and Kivy 3.0 (SDL3). Android recreates an
1384
+ activity for any configuration change the manifest does not declare, and SDL
1385
+ ends the app when its activity is recreated. The generated manifest now
1386
+ declares the same `android:configChanges` list as python-for-android,
1387
+ including `uiMode`, so a theme switch, a language or font-size change, a
1388
+ density change or a split-screen resize reaches the running app instead.
1389
+ - **Migration:** none. An app that set its own `android:configChanges` through
1390
+ `[tool.kivy.android.manifest].activity` keeps its value.
1391
+
1392
+ ### Android: Kivy 2.3.1 apps keep clear of the system bars
1393
+
1394
+ - **Fixed:** on Android 15 and later, an app targeting API 35 or higher is drawn
1395
+ edge to edge, under the status bar and the navigation bar. Kivy 2.3.1 can't
1396
+ report a safe area, so a `kivy_generation = 2` app's content sat under the
1397
+ bars with no way to avoid them. The generated activity now keeps the window
1398
+ clear of the bars and the notch on API 30 and higher, with black behind the
1399
+ bars and light bar icons. The padding follows rotation, and with
1400
+ `fullscreen = true` only the notch is padded. Kivy 3.0 apps are unchanged:
1401
+ they stay edge to edge and pad themselves with `kivy.mobile.get_safe_area()`
1402
+ ([#63](https://github.com/ElliotGarbus/kivyforge/issues/63)).
1403
+ - **Migration:** a generation-2 app's window is now smaller by the bar heights
1404
+ (for example 1080x2216 instead of 1080x2400 on a Pixel 8a). An app that
1405
+ padded itself for the bars, for example by reading the insets with pyjnius,
1406
+ should drop that padding.
1407
+
1408
+ ### Android: `orientation` is honoured at runtime
1409
+
1410
+ - **Fixed:** an Android app rotated to any orientation when the phone's
1411
+ auto-rotate was on, whatever `[tool.kivy].orientation` said. The manifest
1412
+ declared the right orientation, but SDL replaced it when Kivy created its window:
1413
+ with no orientation hint and a resizable window, SDL allows every orientation.
1414
+ The generated activity now passes `[tool.kivy].orientation` to Kivy as
1415
+ `KIVY_ORIENTATION`, the variable python-for-android sets for buildozer apps, so
1416
+ SDL requests what the manifest declares. Applies to Kivy 2.3.1 (SDL2) and
1417
+ Kivy 3.0 (SDL3).
1418
+ - **Migration:** none for most apps. An app that declares `portrait` but relied on
1419
+ rotating anyway must now list the orientations it supports.
1420
+
1421
+ ### Android: `fullscreen` hides the system bars
1422
+
1423
+ - **Added:** `[tool.kivy.android].fullscreen = true` runs the app with the status
1424
+ bar and the navigation bar hidden. A swipe from the edge shows them for a moment.
1425
+ This is the counterpart of buildozer's `fullscreen = 1`, and `kivyforge init`
1426
+ now names it when it finds a `buildozer.spec`. Works on both Kivy 2.3.1 (SDL2)
1427
+ and Kivy 3.0 (SDL3).
1428
+ - **Migration:** none for existing projects: the default is `false`, which is
1429
+ today's behaviour. buildozer treats a missing `fullscreen` as `1`, so an app
1430
+ migrated from buildozer that never set the key needs `fullscreen = true` to keep
1431
+ hiding the bars.
1432
+
1433
+ ### Android and iOS: a failed download is reported, not a traceback
1434
+
1435
+ - **Fixed:** when an Android or iOS build could not download a locked artifact (for
1436
+ example, a wheel host answering `HTTP Error 503`), or the download failed its
1437
+ SHA-256 check, kivyforge crashed with a Python traceback. Under `--json`, stdout
1438
+ was empty instead of one envelope. Both are now reported as an ordinary error
1439
+ naming the artifact's URL and the reason, as the desktop backends already did:
1440
+ `KF-ERROR` and exit `1`, with a `--json` envelope.
1441
+ - **Migration:** none needed. A download error is usually transient: retry the
1442
+ build.
1443
+
1444
+ ### Android builds refuse to start Gradle without a JDK
1445
+
1446
+ - **Fixed:** a `build -p android --debug` (or `package`, `run`) attempted with only a
1447
+ JRE installed started a Gradle daemon, and that daemon outlived the failed build.
1448
+ After installing a JDK at the same path (as Debian's `openjdk-21-jdk-headless`
1449
+ does), the next build still failed with "does not provide the required
1450
+ capabilities: [JAVA_COMPILER]" until `kivyforge clean` stopped the daemon.
1451
+ Every Android verb that runs Gradle now checks first, the same way `doctor`
1452
+ does, and fails before Gradle starts when there is no `java` or the `java` Gradle
1453
+ would run has no `javac` beside it. (Issue #61.)
1454
+ - **Changed:** those two cases now fail with `KF-TOOLCHAIN-MISSING` and exit `3`
1455
+ ("fix the machine"), with `context.tool` set to `java` or `javac`. Before, Gradle
1456
+ ran and failed with `KF-BUILD-TOOL-FAILED` and exit `5`. A JDK of an unsupported
1457
+ version is still reported by Gradle, as before.
1458
+ - **Migration:** a script that treated exit `5` as "no JDK" should check for exit
1459
+ `3` and `KF-TOOLCHAIN-MISSING`. If a daemon from an earlier kivyforge release is
1460
+ already stuck, run `./gradlew --stop` in `<app>-android/` once (or
1461
+ `kivyforge clean`).
1462
+
1463
+ ### `doctor -p android`: fail a JRE with no `javac`
1464
+
1465
+ - **Fixed:** with only a JRE installed (for example Debian's `openjdk-21-jre-headless`),
1466
+ `kivyforge doctor -p android` reported the JDK check as passed, because the
1467
+ version is in range. The build then failed at `compileDebugJavaWithJavac` with
1468
+ "does not provide the required capabilities: [JAVA_COMPILER]". The JDK check now
1469
+ also requires a `javac` beside the `java` Gradle will run (following symlinks, as
1470
+ Debian's `/usr/bin/java` needs), and fails with a hint to install a full JDK.
1471
+ (Issue #47.)
1472
+ - **Migration:** none needed. If `doctor` now fails the JDK check, install a JDK
1473
+ (`sudo apt install openjdk-21-jdk-headless` on Debian or Ubuntu) and point
1474
+ `JAVA_HOME` at it.
1475
+
1476
+ ### Android: BOMs and Gradle plugins (enables Firebase)
1477
+
1478
+ - **Added:** `[tool.kivy.android.gradle].platforms` lists BOM coordinates, such as
1479
+ `com.google.firebase:firebase-bom:34.15.0`. Each is imported with Gradle's
1480
+ `platform()`, and a `dependencies` entry may then omit its version
1481
+ (`com.google.firebase:firebase-analytics`). `kivyforge lock` records the version
1482
+ the BOM chose for each library, with its SHA-256. The BOM needs an exact version.
1483
+ - **Added:** `[tool.kivy.android.gradle.plugins]` maps a Gradle plugin ID to an
1484
+ exact version, for example `"com.google.gms.google-services" = "4.4.2"`. Plugins
1485
+ are applied after the Android plugin, in the order listed. With
1486
+ `include_files` staging `google-services.json`, this is everything Firebase
1487
+ needs. A plugin's version is pinned, but no hash is recorded for it, the same as
1488
+ for the Android Gradle plugin.
1489
+ - **Migration:** none needed. Projects without these keys generate the same
1490
+ Gradle files and the same lock as before.
1491
+
1492
+ ### `doctor`: no more iOS failures off a Mac
1493
+
1494
+ - **Fixed:** `kivyforge doctor` with no `-p` on a project whose only overlay is
1495
+ `[tool.kivy.android]` (or any single non-host platform) fell back to iOS and reported
1496
+ `missing [tool.kivy.ios] table`, `Xcode not found` and `xcode-select path not set`,
1497
+ none of which the user can or should act on. It now uses the project's only
1498
+ configured platform, and for a bare environment check or an ambiguous project on
1499
+ Windows or Linux, the host's own platform. iOS remains the fallback on macOS.
1500
+ - **Migration:** none needed. Pass `-p` or set `KIVYFORGE_PLATFORM` to pick a platform
1501
+ explicitly, as before.
1502
+
1503
+ ### Android: SDK license guidance for command-line tools 23+
1504
+
1505
+ - **Fixed:** the `doctor` "SDK licenses" hint, the README, and the Android quickstart
1506
+ told you to run `sdkmanager --licenses`. In command-line tools 23 and later
1507
+ `sdkmanager` is a wrapper around Google's Android CLI, and `--licenses` does nothing
1508
+ (exit 0, no license recorded), so the warning never cleared. The hint now says to
1509
+ install any package (such as `sdkmanager platform-tools`), which records the license;
1510
+ `--licenses` is still named for older tools.
1511
+ - **Docs:** the quickstart has a new "SDK licenses and `sdkmanager`" section. It also
1512
+ notes that the new `sdkmanager` exits 0 when an install fails, and that the Android
1513
+ CLI collects usage data unless run with `--no-metrics`.
1514
+ - **CI:** the jobs that install the pinned NDK, CMake, platform, and build-tools now
1515
+ assert each package landed, because a failed install no longer fails the step.
1516
+ - **Migration:** none needed.
1517
+
1518
+ ### Android: doctor checks CMake
1519
+
1520
+ - **Added:** `kivyforge doctor` now checks for the CMake the native launcher
1521
+ compile needs. It used to be missing from the checks, so Gradle downloaded it
1522
+ silently during the first build. A missing CMake is a warning with the exact
1523
+ `sdkmanager 'cmake;3.22.1'` command.
1524
+ - **Changed:** the generated `app/build.gradle` pins `externalNativeBuild.cmake.version`
1525
+ to `3.22.1` (AGP's own default), so the build and `doctor` ask for the same package.
1526
+ - **Migration:** none needed. A build still downloads CMake itself when it is missing
1527
+ and the SDK licenses are accepted.
1528
+
1529
+ ### Android: compile Java at level 17
1530
+
1531
+ - **Changed:** the generated `app/build.gradle` now sets `sourceCompatibility` and
1532
+ `targetCompatibility` to `VERSION_17` (was `VERSION_1_8`). JDK 21+ reports
1533
+ source/target 8 as obsolete on every build; kivyforge already requires JDK 17+,
1534
+ so no new JDK is needed.
1535
+ - **Migration:** none needed. Regenerate the project (`kivyforge build`) to pick it up.
1536
+
1537
+ ### Android: accept pyjnius 1.8
1538
+
1539
+ - **Changed:** the bootstrap's pyjnius `invoke0` range is now `>=1.7.0,<1.9`
1540
+ (was `<1.8`). pyjnius 1.8.0 is the first release with Android wheels on PyPI, so
1541
+ a bare `pyjnius` dependency re-locks to it, and `build` and `doctor` used to
1542
+ reject that lock.
1543
+ - **Fixed:** the `build`/`doctor` message for an out-of-range pyjnius said to
1544
+ re-lock, which picks the same version again. It now says to constrain pyjnius in
1545
+ `[project].dependencies`.
1546
+ - **Migration:** none needed. Projects locked to 1.7.x keep working.
1547
+
1548
+ ### `lock` (Android): warn when `kivy_generation` disagrees with the locked Kivy
1549
+
1550
+ - **Added:** `kivyforge lock -p android` now warns (and records a `KF-LOCK-WARNING`
1551
+ diagnostic under `--json`) when `[tool.kivy.android].kivy_generation` does not
1552
+ match the Kivy it resolved, for example `kivy_generation = 3` locking Kivy
1553
+ 2.3.1. Previously only `doctor` noticed, after the lock was written. The lock is
1554
+ still written.
1555
+ - **Docs:** `kivy_generation` selects the SDL bootstrap only; it never influences
1556
+ resolution. The Kivy version comes from `[project].dependencies`, so Kivy 3.0
1557
+ needs a requirement that admits it, such as `"kivy>=3.0.0.dev0,<4"`.
1558
+
1559
+ ### `init`: documented `[tool.kivy]`, and templates that fit each platform
1560
+
1561
+ - **Changed:** `[tool.kivy]` now carries a comment on every key.
1562
+ `orientation` is written only when a mobile platform is being seeded, since
1563
+ only the iOS and Android backends read it. Seeding several platforms at once
1564
+ now writes iOS and Android first, so the shared table keeps `orientation`.
1565
+ - **Changed:** the `exclude` list in the desktop (macOS, Linux, Windows)
1566
+ templates is now an optional, commented-out size trim. It used to be active
1567
+ and dropped `requests` and its dependencies, which breaks `UrlRequest`. iOS
1568
+ and Android keep the active list.
1569
+ - **Changed:** the iOS and Android templates write `extra_index_urls` for the
1570
+ kivy-mobile-wheels index uncommented. Without it `kivyforge lock` cannot find
1571
+ a Kivy wheel. `init` now assumes Kivy is a dependency: the `exclude` list and
1572
+ this line are always written, instead of only when `[project].dependencies`
1573
+ names `kivy`. The Linux template notes that `archs = ["aarch64"]` targets a
1574
+ Raspberry Pi.
1575
+ - **Fixed:** the "no pyproject.toml" and "found requirements.txt" messages
1576
+ suggested `kivy>=3.0`, which has no desktop wheel yet. They now suggest
1577
+ `kivy>=2.3.1` and say an iOS app needs Kivy 3.0.
1578
+ - **Migration:** existing projects are untouched. A project that relied on the
1579
+ desktop `exclude` default should add it back by hand.
1580
+
1581
+ ### `lock`: the resolver error names the package and says what to do
1582
+
1583
+ - **Changed:** when pip cannot resolve a dependency for a mobile or desktop
1584
+ target, `kivyforge lock` now names the package in its own headline (for
1585
+ example `no android_24_arm64_v8a wheel for 'numpy'`) instead of leaving it in
1586
+ pip's output further down. For Android it first suggests the first-party
1587
+ kivy-mobile-wheels index (`extra_index_urls`) when it is not configured, and
1588
+ otherwise `find_links` or dropping the dependency. The iOS error gives the
1589
+ same index hint. Error codes are unchanged.
1590
+ - **Docs:** the README's Android quick start now explains that Kivy has no
1591
+ Android wheel on PyPI, shows the `extra_index_urls` line, and says `lock` uses
1592
+ kivyforge's own pip, not the app environment's. The `init` template comment
1593
+ for that line no longer says "until they are on PyPI".
1594
+
1595
+ ### `run`: progress goes to stderr
1596
+
1597
+ - **Changed:** `kivyforge run` printed its own progress (`[run]`/`[smoke]`
1598
+ lines, `Installing on ...`, `Launching ...`, and every line of the build it
1599
+ runs first) to stdout. That now goes to stderr, as it does for `build` and
1600
+ `package`. stdout carries only what the command produces: the app's own
1601
+ output on desktop, the app's filtered logcat on Android, the app console on
1602
+ iOS, and "Contract smoke test PASSED." for `--smoke`. On iOS the app's
1603
+ output arrives as Apple's launch tool delivers it: the simulator merges the
1604
+ app's stdout and stderr onto stdout, while a device keeps them apart, so
1605
+ Kivy's log (which Kivy writes to stderr) shows on stderr, and `devicectl`
1606
+ adds two lines of its own to stdout.
1607
+ - **Migration:** a script that captured `run`'s stdout to read build progress
1608
+ should read stderr instead. One that only checked the exit status, or read
1609
+ the app's output or the smoke verdict, is unaffected.
1610
+
1611
+ ### Android: App Bundles are now checked in CI
1612
+
1613
+ - Google Play requires an App Bundle (`.aab`) for new apps, but no CI job
1614
+ built one: every Android job packaged an APK, one ABI at a time. A new
1615
+ `android_bundle` job now runs `kivyforge package -p android -f aab` with both
1616
+ default ABIs and checks the result: every ABI complete and shipping the same
1617
+ native libraries, the byte-compiled payload, the merged manifest, the
1618
+ bundle's signature (`jarsigner -verify`), and that the APKs Play generates
1619
+ will still extract native libraries, which kivyforge's Python loader
1620
+ requires.
1621
+ - The post-build checks now understand packages with more than one ABI. A
1622
+ correct two-ABI package used to fail them.
1623
+ - Test-only; no change to what kivyforge builds.
1624
+
1625
+ ### Android `doctor` checks the JDK's version and finds the SDK on macOS
1626
+
1627
+ - **Fixed:** `doctor -p android` passed any `java` that existed. On a Mac
1628
+ with no JDK, Apple's `/usr/bin/java` stub passed, and so did Android
1629
+ Studio's bundled JDK 25, which the pinned Gradle 8.11.1 cannot run on. The
1630
+ build then failed with "Unsupported class file major version". `doctor` now
1631
+ runs `java -version` and requires a JDK from 17 to 23.
1632
+ - **Fixed:** with `ANDROID_HOME` unset, kivyforge did not look in
1633
+ `~/Library/Android/sdk`, where Android Studio installs the SDK on macOS.
1634
+ `doctor`, the build's `sdk.dir` and `run`'s `adb` lookup now find it.
1635
+ - The docs said "JDK 17 or later". The range is now stated once, in the
1636
+ Android supported-versions table, which the other pages link to. Those
1637
+ pages note that Android Studio's bundled JDK may fall outside it and that
1638
+ `doctor` checks.
1639
+ - **Fixed:** `run -p android --smoke` without `--release` staged the release
1640
+ payload, so with the default `byte_compile = "release"` it tested
1641
+ byte-compiled, stripped Python inside a debug APK. It now stages the debug
1642
+ payload, the same Python `run` installs. `--smoke --release` is unchanged.
1643
+
1644
+ ### The missing-`pyproject.toml` hint no longer points at `init` to create one
1645
+
1646
+ - **Fixed:** a verb run without a `pyproject.toml` suggested running
1647
+ `kivyforge init` to create one, but `init` does not create one: it stops
1648
+ and asks for a `[project]` table first. The hint now says to write the
1649
+ `[project]` table, then run `kivyforge init -p <platform>`.
1650
+
1651
+ ### The dice-roller example no longer names the author's Developer ID
1652
+
1653
+ - **Changed:** `examples/desktop/dice-roller` no longer has a
1654
+ `[tool.kivy.macos.signing]` table, so `package -p macos` ad-hoc signs it
1655
+ on any Mac. Before, the table named the author's certificate, and the
1656
+ desktop quickstart told readers to delete it before locking. That step is
1657
+ gone.
1658
+
1659
+ ### A `pyproject.toml` saved with a byte-order mark loads
1660
+
1661
+ - **Fixed:** a `pyproject.toml` that starts with a UTF-8 byte-order mark
1662
+ failed with "invalid TOML: Invalid statement (at line 1, column 1)". Some
1663
+ editors write one, and so does PowerShell's `-Encoding utf8`. kivyforge
1664
+ now ignores it.
1665
+ - Adding or removing the mark does not make an existing lock stale.
1666
+
1667
+ ### Pillow is a core dependency; the `linux`, `windows` and `android` extras are gone
1668
+
1669
+ - **Changed:** Pillow is now installed with kivyforge. Before, a project that
1670
+ set an icon or splash image for Android, Linux or Windows needed the
1671
+ matching extra, and without it the build stopped partway through.
1672
+ - **Removed:** the `linux`, `windows` and `android` extras. Their only
1673
+ content was Pillow.
1674
+ - **Migration:** install plain `kivyforge`. An old command such as
1675
+ `pip install "kivyforge[android]"` still works: pip warns that the extra
1676
+ does not exist and installs kivyforge with Pillow.
1677
+
1678
+ ### `init`: the buildozer.spec key map names real keys
1679
+
1680
+ - **Fixed:** when `init` finds a `buildozer.spec` and no `pyproject.toml`,
1681
+ it prints how each buildozer setting maps onto kivyforge. Several entries
1682
+ pointed at keys kivyforge does not read:
1683
+ - `android.gradle_dependencies` now maps to `[tool.kivy.android.gradle].dependencies`.
1684
+ - `android.add_jars` now maps to `[tool.kivy.android.native.jars]`.
1685
+ - `android.add_activities` now maps to `[[tool.kivy.android.activities]]`.
1686
+ - `android.add_src` is no longer listed as unsupported. It maps to
1687
+ `[tool.kivy.android.src].java`.
1688
+ - The map now also covers `android.add_aars`,
1689
+ `android.add_gradle_repositories`, `android.add_assets`,
1690
+ `android.add_resources`, `android.manifest.intent_filters`,
1691
+ `android.manifest_placeholders`, `android.allow_backup`,
1692
+ `android.apptheme`, `android.numeric_version`,
1693
+ `android.presplash_color` and the adaptive icon layers.
1694
+ - The closing reference link now points at the user reference,
1695
+ `docs/guides/reference/pyproject/android.md`.
1696
+
1697
+ ### iOS: `run --device` builds for the phone it installs on
1698
+
1699
+ - **Fixed:** with automatic signing, `run -p ios --device` could build and
1700
+ sign successfully, then fail to install with "This provisioning profile
1701
+ cannot be installed on this device". This happened on any phone added
1702
+ after Xcode last created the managed profile. The build targeted any iOS
1703
+ device, so Xcode kept the old profile. `run --device` now chooses the phone
1704
+ before building and passes it to `xcodebuild` as `-destination`, so Xcode
1705
+ adds the phone to the managed profile, or stops the build with an error
1706
+ naming the phone.
1707
+ - A missing phone is now reported before the build instead of after it.
1708
+ `build --device` has no target phone and is unchanged.
1709
+ - Adding a phone to the profile requires an Apple account signed in to
1710
+ Xcode (**Xcode > Settings > Accounts**). Without one, the build now fails
1711
+ with Xcode's "No Accounts" error instead of producing an app the phone
1712
+ refuses.
1713
+
1714
+ ### iOS: unpinning a provisioning profile takes effect
1715
+
1716
+ - **Fixed:** after removing `provisioning_profile` (or clearing `team_id`),
1717
+ the next build still carried the old value in the generated `.xcodeproj`,
1718
+ which kivyforge updates in place rather than recreating. With
1719
+ `auto_signing = true`, Xcode then failed with "conflicting provisioning
1720
+ settings". This is the fix kivyforge itself suggests when it refuses an
1721
+ Xcode-managed profile. Build settings kivyforge owns are now removed from
1722
+ the project when the configuration no longer sets them. Settings kivyforge
1723
+ does not own, including any set in Xcode's editor, are left alone.
1724
+ - **Migration:** none. A project left in this state is repaired by the next
1725
+ `build`; deleting `<app>-ios/` is no longer needed.
1726
+
1727
+ ### iOS: `run --device` finds the phone on Xcode 27
1728
+
1729
+ - **Fixed:** with Xcode 27, `run -p ios --device` refused a single connected
1730
+ phone with "multiple paired iOS devices found", because Xcode 27's
1731
+ `devicectl` also lists simulators as paired iOS devices. `--device` now
1732
+ ignores simulators, so one connected phone is picked without
1733
+ `--destination`.
1734
+ - **Fixed:** `--destination` rejected the UDID that `run --list-devices`
1735
+ prints. It now accepts the UDID, as well as the name and devicectl's own
1736
+ identifier.
1737
+ - `--device --destination` matches only iOS devices, so a name fragment such
1738
+ as your first name no longer picks a paired Apple Watch. A destination that
1739
+ names a simulator now says to use `--simulator` instead of "no device
1740
+ matches".
1741
+
1742
+ ### iOS: a pinned profile Xcode will refuse stops the build up front
1743
+
1744
+ - **Changed:** a device or release build, `run --device`, and `package` now
1745
+ stop before `xcodebuild` in two cases where Xcode refuses the pinned profile.
1746
+ Before, both built the whole app and then failed (exit 5) with Xcode's error,
1747
+ while `doctor` reported PASS:
1748
+ - `provisioning_profile` set with `auto_signing = true` (the default). Xcode
1749
+ rejects a pinned profile under automatic signing.
1750
+ - an Xcode-managed profile (`iOS Team Provisioning Profile: ...`, the kind
1751
+ automatic signing installs) pinned under `auto_signing = false`. Manual
1752
+ signing requires a profile created at developer.apple.com.
1753
+ - `doctor -p ios` now FAILs the Provisioning profile check in both cases.
1754
+ - `package` now runs the entitlements check that `build` already ran, so an
1755
+ entitlement the pinned profile does not grant fails before the archive
1756
+ instead of at export.
1757
+ - **`KF-ENTITLEMENTS-UNGRANTED` is retired.** It warned about ungranted
1758
+ entitlements under automatic signing with a pinned profile, a combination
1759
+ that never built. It is no longer emitted and will not be reused.
1760
+ - **Migration:** if you pin `provisioning_profile`, set `auto_signing = false`
1761
+ and pin a manually created profile; or remove `provisioning_profile` and let
1762
+ Xcode manage signing. Re-lock afterwards. Simulator builds are unaffected.
1763
+
1764
+ ### iOS: `run` streams the app's console
1765
+
1766
+ - **Fixed:** `run -p ios` launched the app but never showed its output, and
1767
+ Ctrl+C discarded it; the quickstart said it streamed. The launch now runs
1768
+ attached to your terminal, as macOS `run` does, until the app exits or you
1769
+ press Ctrl+C. On a device the launch passes `devicectl --console`, which
1770
+ attaches to the app's output and waits for it to exit.
1771
+
1772
+ ### iOS: `--arch` offers only the simulator slice kivyforge pins
1773
+
1774
+ - **Fixed:** `build -p ios --arch aarch64` suggested `x86_64`, which then
1775
+ crashed with a traceback (no `--json` envelope) because no lock pins an
1776
+ x86_64 simulator slice. The error now suggests `arm64` only.
1777
+ - On an Intel Mac, a simulator build fails before downloading anything, with
1778
+ exit 3 (`KF-HOST-INCAPABLE`), and says to build for a device or on Apple
1779
+ Silicon. Device builds on an Intel Mac are unaffected.
1780
+ - A wheel the lock does not cover for the requested slice is now a
1781
+ configuration error with a `--json` envelope, and says to re-lock.
1782
+
1783
+ ### macOS and Linux: the built bundle is readable by other users
1784
+
1785
+ - **Fixed:** the macOS `.app` and the Linux AppDir were created mode `0700`,
1786
+ so only the user who built them could open them, and `cp -R` carried that
1787
+ into a DMG or `/Applications`. They now get the mode a plain `mkdir` would
1788
+ (`0755` under the usual umask). Rebuild to pick it up.
1789
+
1790
+ ### CLI: help text matches what each option does
1791
+
1792
+ - **Fixed:** the top-level help called kivyforge "a declarative iOS bundler";
1793
+ it now names all five platforms. Its em dash also printed as `ΓÇö` when help
1794
+ was piped on Windows; help text is now ASCII-only, and a test keeps it so.
1795
+ - `open` help, and its error on platforms without an IDE project, now say it
1796
+ opens Xcode for iOS and Android Studio for Android.
1797
+ - `--team-id`, `--signing-identity`, and `--export-method` now say which
1798
+ platforms read them. `package --signing-identity` applies to iOS and macOS
1799
+ only; Windows signs with `[tool.kivy.windows.signing].thumbprint`, and
1800
+ Android with `--keystore`.
1801
+ - **Changed:** `build` and `package` now refuse those options, and `package`'s
1802
+ `--notarize`/`--no-notarize`/`--notary-profile`, on a platform that does not
1803
+ read them, as they already refused Android-only options elsewhere. Before,
1804
+ `package -p windows --signing-identity X` succeeded and signed nothing with
1805
+ X. The error names each option and the platforms it applies to (exit 1).
1806
+ - **Migration:** a script that passes one of these to a platform that ignores
1807
+ it, such as one command line shared across targets, now fails; drop the
1808
+ option for that platform. Nothing it did changes: the option was never
1809
+ applied there.
1810
+
1811
+ ### Config: unknown keys under `[tool.kivy]` are errors
1812
+
1813
+ - **Changed:** a key kivyforge does not recognise anywhere under `[tool.kivy]`
1814
+ now fails every verb with a configuration error (exit 1). Before, it was
1815
+ dropped without a word and the build went ahead as if it had never been
1816
+ written: a typo such as `deployment_targt`, or `[tool.kivy.android.proguard]`,
1817
+ which the Android spec describes but no release implements, so a release
1818
+ build shipped with none of the keep rules it declared.
1819
+ - The error names the key and its line, and suggests the closest valid key
1820
+ (`did you mean 'deployment_target'?`) or lists the valid ones. All unknown
1821
+ keys are reported in one run.
1822
+ - Not checked: keys outside `[tool.kivy]`; tables passed through to another
1823
+ tool (`info_plist`, `entitlements`, `xcode.build_settings`, the Android
1824
+ `manifest` attribute tables, `gradle_properties`); and a table for a platform
1825
+ kivyforge does not support yet, such as `[tool.kivy.web]`, which spec 01
1826
+ promises is inert. Targeting a misspelt overlay now names it:
1827
+ `found [tool.kivy.andriod]; rename it to [tool.kivy.android]`.
1828
+ - **Migration:** a project with an unknown key stops building until the key
1829
+ is fixed or removed. No setting changes meaning: every key the error names
1830
+ was already being ignored. Remove `[tool.kivy.android.proguard]` if you
1831
+ set it; it never took effect.
1832
+
1833
+ ### Windows: `package` warns when a bundle only fits in a short folder
1834
+
1835
+ - Windows limits a path to 259 characters unless long paths are enabled, and
1836
+ they are **off** by default. Every file in an app has to fit, *including*
1837
+ the folder the user unzips or installs it into, so the deepest path inside
1838
+ the bundle decides how long that folder can be.
1839
+ - `kivyforge package -p windows` now measures this. When the bundle leaves
1840
+ less than 100 characters for its folder, it prints a warning naming the
1841
+ deepest path and the longest folder that will work, and reports
1842
+ **`KF-PATH-DEPTH`** in `--json` output. A typical Kivy app leaves about 154,
1843
+ so most projects will never see it; deep dependency trees can.
1844
+ - Measured, not estimated: with long paths off, a bundle in a folder of
1845
+ exactly the reported length works, and one character longer does not.
1846
+ - No change to the launcher. Declaring it `longPathAware` would not help:
1847
+ Windows refuses to start an executable from a folder too long for its own
1848
+ path even with long paths enabled, and the bundled `python.exe`, which does
1849
+ the file access, already opts in.
1850
+
1851
+ ### iOS: `provisioning_profile` works as documented
1852
+
1853
+ - **Fixed:** `[tool.kivy.ios.signing].provisioning_profile` was read two
1854
+ incompatible ways. Xcode received it as a profile name or UUID (the
1855
+ documented form), but `doctor` and the entitlements pre-flight treated it as
1856
+ a file path. A name or UUID made `doctor` FAIL ("not found") and silently
1857
+ skipped the entitlements check; a path passed `doctor` and then failed the
1858
+ Xcode build.
1859
+ - A name or UUID is now looked up among the profiles installed on the Mac, so
1860
+ `doctor` and the entitlements check use the same profile Xcode signs with.
1861
+ - A path to a `.mobileprovision` is also accepted: kivyforge reads the file and
1862
+ passes its UUID to Xcode. `doctor` warns if that profile is not installed,
1863
+ since Xcode signs only with installed profiles.
1864
+ - **Migration:** none for the documented name/UUID form. If you set a path,
1865
+ it keeps working, and now also reaches Xcode correctly.
1866
+
1867
+ ### Linux: `--arch aarch64` is accepted
1868
+
1869
+ - **Fixed:** `build` and `package` rejected `--arch aarch64` as a usage error,
1870
+ although the Linux spec documents `package -p linux --arch aarch64`. With
1871
+ `archs = ["x86_64", "aarch64"]` the Raspberry Pi AppImage could only be
1872
+ produced by reordering the list. The shared `--arch` choices now include
1873
+ `aarch64`; with no `--arch`, the first locked arch is still the default.
1874
+ - `run` still does not accept `aarch64`: that arch is cross-built only, and
1875
+ `run` launches the result on the build host.
1876
+ - `build -p ios --arch aarch64` now fails with a clear message instead of
1877
+ deriving a nonexistent iOS platform tag.
1878
+
1879
+ ### Install: pip is now a dependency
1880
+
1881
+ - **Fixed:** `uv tool install kivyforge` produced an install where every
1882
+ `kivyforge lock` failed. kivyforge runs `python -m pip` to resolve and stage
1883
+ wheels, and uv tool environments ship without pip. The failure was also
1884
+ misleading: it reported that a dependency had no wheel upstream.
1885
+ - kivyforge now depends on `pip>=25.1` (the Android resolver's floor, which
1886
+ also covers iOS's 24.3), so every installer, including uv and pipx, gets a
1887
+ pip new enough for every target.
1888
+
1889
+ ### Docs: end-user documentation site (MkDocs Material)
1890
+
1891
+ - **Added:** a task-based user guide — installation, per-platform quickstarts,
1892
+ concepts, per-platform how-to guides, a full configuration/CLI reference, and
1893
+ troubleshooting — built with MkDocs Material and published to GitHub Pages
1894
+ (roadmap item 6). Sources live in `docs/guides/`; the design documents under
1895
+ `docs/design/` stay unpublished.
1896
+ - The host matrix, exit codes, and `KF-*` diagnostic vocabulary are generated at
1897
+ build time from kivyforge's own `capabilities`/`report` modules, and the CLI
1898
+ reference from the click command tree, so the site cannot drift from the tool.
1899
+ `mkdocs build --strict` runs in CI and fails on a broken internal link, a
1900
+ missing nav page, or a `KF-*` code the reference does not document.
1901
+
1902
+ ### Desktop: clear error when the runtime can't be staged on a case-insensitive filesystem
1903
+
1904
+ - **Fixed:** every python-build-standalone Linux runtime ships 25 pairs of
1905
+ paths that differ only by case, all in `python/share/terminfo`
1906
+ (`h/hp70092A` beside `h/hp70092a`). Building a Linux app onto a
1907
+ case-insensitive filesystem, such as `/mnt/c` from WSL2, used to die partway
1908
+ through staging with a bare `shutil.Error`. Worse, extracting straight onto
1909
+ such a filesystem silently overwrote one file of each pair.
1910
+ - Staging now checks the runtime archive **before** extracting it, and stops
1911
+ with the number of colliding paths, an example pair, the directory, and what
1912
+ to do instead (under WSL2: build from the Linux side, e.g. under `~`).
1913
+ - Applies to the Linux, macOS and Windows runtime stagers. The filesystem is
1914
+ only probed when an archive actually has such collisions; no macOS or
1915
+ Windows runtime measured so far does, so those builds are unaffected.
1916
+
1917
+ ### Desktop: `kivyforge run --release` (Linux, macOS, Windows)
1918
+
1919
+ - **`run --release` now works on the desktop backends**, as it has on Android
1920
+ since the entry below. Previously `run` on Linux, macOS and Windows always
1921
+ built the dev flavor, so `byte_compile`/`strip_source` (both release-only by
1922
+ default) could only be reached through `kivyforge package` — never through
1923
+ the command used for day-to-day iteration. That gap is where the Linux
1924
+ `AppRun` defect lived: shipped, reproducible in one command, and invisible
1925
+ to every verb a developer runs while working.
1926
+ - The release flavor is built into the same `build/<platform>/` tree `run`
1927
+ always launches, so a later plain `run` rebuilds it with readable source.
1928
+ - `--release --no-build` is refused: `--no-build` launches whatever the last
1929
+ build left, and nothing records whether that was a release build.
1930
+ - `run -p ios --release` is refused rather than ignored — an iOS release build
1931
+ is an archive and a signed `.ipa`, not a simulator install. Use
1932
+ `kivyforge build --release` or `kivyforge package -p ios`.
1933
+
1934
+ ### `kivyforge lock` honors `GH_TOKEN` / `GITHUB_TOKEN`
1935
+
1936
+ - **Fixed:** resolving the python-build-standalone runtime queries GitHub's
1937
+ API, which allows only **60 requests/hour per IP** unauthenticated — enough
1938
+ for a dev box, not for CI, where a shared runner egress address can exhaust
1939
+ it and fail the lock with `HTTP Error 403: rate limit`. A single resolve can
1940
+ spend up to 16 of those requests.
1941
+ - `lock` (and `lock --check`, which re-resolves) now sends `GH_TOKEN` or
1942
+ `GITHUB_TOKEN` as a bearer token when either is set, raising the ceiling to
1943
+ 5000/hour. `GH_TOKEN` takes precedence, matching the `gh` CLI. An empty or
1944
+ whitespace-only value is ignored rather than sent, so an unset CI secret
1945
+ degrades to an anonymous request instead of a 401.
1946
+ - No configuration is needed to keep the old behavior: with neither variable
1947
+ set, the request is anonymous exactly as before.
1948
+ - On GitHub Actions, `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` needs no
1949
+ setup. A rate-limited failure now says so in its error message.
1950
+
1951
+ ### Android: manifest passthrough values were escaped twice
1952
+
1953
+ - **Fixed:** a value in `[tool.kivy.android.manifest.application]` or
1954
+ `[tool.kivy.android.manifest.activity]` containing `&`, `<` or `>` reached
1955
+ the device with the escape still in it — `android:description = "Rock &
1956
+ Roll"` shipped as the literal text `Rock &amp; Roll`. The value was escaped
1957
+ on its way into the attribute and then escaped again when the attribute was
1958
+ quoted. Values without those three characters were never affected, which is
1959
+ every in-repo example and most real configs.
1960
+ - Found by the new merged-manifest T3 check (below) on its first run.
1961
+
1962
+ ### Android: two new post-build checks in CI
1963
+
1964
+ - The `android_gradle` job now asserts that **AGP's merged release manifest
1965
+ still contains what `pyproject.toml` declared** — applicationId, version
1966
+ code/name, min/target SDK, every declared permission, feature, service,
1967
+ extra activity and intent-filter, and that no `${placeholder}` survived
1968
+ unresolved. The release policy pass already read this manifest to lint its
1969
+ *posture*; nothing checked that the project's own declarations had survived
1970
+ the merge.
1971
+ - The same job now runs **`apksigner verify` on the release APK** at the
1972
+ project's own `min_sdk`, and checks the v1 (JAR) signing scheme against
1973
+ `[tool.kivy.android.signing].v1_signing`.
1974
+ - Both are test-only additions; no behavior change for builds that were
1975
+ already correct.
1976
+
1977
+ ### Android: `kivyforge run --release`
1978
+
1979
+ - **`run --release` now builds and launches the release variant**, not just
1980
+ `--smoke --release`. Previously `run` always built debug, so `byte_compile`/
1981
+ `strip_source` (both release-only by default) could only ever be exercised
1982
+ through `kivyforge package` — never through the command developers actually
1983
+ use for day-to-day iteration.
1984
+ - Skips `package`'s manifest-policy and lint gates (those belong to the
1985
+ distribution path); signing falls back to the debug keystore when release
1986
+ signing isn't configured, so `run --release` never requires release secrets
1987
+ to work.
1988
+
1989
+ ### iOS: a Swift Package with a signable target (e.g. Firebase) now builds for device/release
1990
+
1991
+ - **Fixed:** an iOS app with a Swift Package dependency that has its own
1992
+ signable target (e.g. Firebase's resource-bundle targets, `Firebase_FirebaseCore`)
1993
+ could fail to build with `Signing for "..." requires a development team`,
1994
+ even with automatic signing and a team correctly configured — the app's own
1995
+ target built and signed fine, but Xcode builds a Swift Package's targets as
1996
+ a *separate, synthesized sub-project* that does not inherit signing from
1997
+ either the consuming target's or the project's `.pbxproj` build settings.
1998
+ `DEVELOPMENT_TEAM` now also goes out as an `xcodebuild` command-line
1999
+ override (as `CODE_SIGN_IDENTITY` already did) for `--device` builds and
2000
+ `--release` archives, which is what the package's own targets actually
2001
+ read.
2002
+ - An earlier attempt at this fix (also writing `DEVELOPMENT_TEAM`/
2003
+ `CODE_SIGN_STYLE` to the project's own build configuration, matching what
2004
+ Xcode's UI does when you set a Team in Signing & Capabilities) is kept —
2005
+ it's harmless and correct for the app's own target — but was verified,
2006
+ against the real `firebase-ios-sdk` package, to be **insufficient on its
2007
+ own**: the identical error still reproduced with only that fix in place.
2008
+ - Reported by an early user after adding the Firebase SPM package; reproduced
2009
+ and confirmed fixed against the real package (`FirebaseCore` product,
2010
+ `firebase-ios-sdk` 11.15.0) on a real device build, 2026-09-21.
2011
+
2012
+ ### iOS: a remote Swift Package no longer embeds by default
2013
+
2014
+ - **Changed:** `[tool.kivy.ios.native.swift_packages]`'s `embed` field now
2015
+ defaults to `false` for a `url` (remote) package; a `path` (local) package
2016
+ is unchanged and still defaults to `true`. A remote package's library
2017
+ product is almost always `automatic`, which Xcode resolves to **static**
2018
+ for a standalone consuming target — there's no `.framework` for Xcode to
2019
+ copy, so the old `embed = true` default failed at `xcodebuild build` with
2020
+ `The file "<Product>" couldn't be opened because there is no such file`
2021
+ for most third-party packages, Firebase's included. A local `path` shim is
2022
+ author-controlled (its own `Package.swift` can deliberately declare
2023
+ `type: .dynamic`) and keeps the old default.
2024
+ - **Migration:** if a remote package's product really is a dynamic framework
2025
+ (check its own docs — Sentry's `Sentry` product is one example), set
2026
+ `embed = true` explicitly; it no longer comes for free.
2027
+ - Reported by an early user after adding `FirebaseAuth`; reproduced against
2028
+ the real package and confirmed `embed = false` fixes it. See
2029
+ `docs/design/dev/ios-firebase-spm-signing-findings.md` for the full
2030
+ investigation, including why a reactive fix (matching `xcodebuild`'s error
2031
+ text) was rejected as fragile in favor of this proactive default.
2032
+
2033
+ ### iOS: a missing SDL3 now fails the build, not just the launch
2034
+
2035
+ - **A Kivy iOS app that stages `SDL3.xcframework` now fails to *compile*** if
2036
+ its SDL3 headers are somehow missing (a broken vendoring step, a manually
2037
+ edited `HEADER_SEARCH_PATHS`), with an actionable error naming the cause.
2038
+ Previously this silently compiled the headless (windowless) fallback path
2039
+ instead — an app that launches but can never open a window, with nothing
2040
+ in the build log explaining why.
2041
+ - Non-Kivy (pure-Python) apps are unaffected; the guard only activates when
2042
+ SDL3.xcframework is actually staged.
2043
+ - Credit: [PR #1](https://github.com/ElliotGarbus/kivyforge/pull/1) (kengoon).
2044
+
2045
+ ### `kivyforge init -p` is now repeatable
2046
+
2047
+ - **Seed several platforms in one run:** `kivyforge init -p ios -p android`
2048
+ writes both overlays into `pyproject.toml` in a single call — the shared
2049
+ `[tool.kivy]` table is written once, and each platform's own overlay is
2050
+ added or (with `--force`) regenerated in turn. Previously this took one
2051
+ `init` invocation per platform.
2052
+ - **`--json`'s shape is unchanged for the common, single-`-p` case**
2053
+ (`platform` + `data.action`/`data.tables`). With more than one `-p`, the
2054
+ envelope's `platform` is `null` and results are listed under
2055
+ `data.results` as `{"platform", "action", "tables"}` per platform, in the
2056
+ order given.
2057
+ - Every other verb (`build`, `run`, `package`, `doctor`, `status`, ...)
2058
+ is unaffected — their `-p` still takes exactly one value, as it must.
2059
+
2060
+ ### `entry_point` now runs as `__main__` on every platform
2061
+
2062
+ - **Migration note.** `if __name__ == "__main__":` blocks in your entry
2063
+ module now execute on Android and iOS, matching the behavior Linux, macOS
2064
+ and Windows already had. Previously the entry module was only *imported* on
2065
+ mobile, so that idiom silently never ran there — no error, nothing in the
2066
+ log. If your app already calls `App().run()` unconditionally at module top
2067
+ level (the style every in-repo example uses), nothing changes for you.
2068
+ - **Breaking for one specific pattern:** an `entry_point` pointing at a
2069
+ *package* (not a module) now requires a `__main__.py` on every platform,
2070
+ same as `python -m mypackage`. Before this change, Android and iOS ran the
2071
+ package's `__init__.py` instead. Add a `__main__.py`, or point `entry_point`
2072
+ at an explicitly-named module instead (e.g. `entry_point = "myapp.start"`).
2073
+ - **Also changes:** the entry module is no longer left in `sys.modules` after
2074
+ startup (mobile only — desktop already worked this way). Code that expects
2075
+ to `import` its own entry module again by name after startup will now
2076
+ re-execute it instead of getting the cached one.
2077
+ - Implementation: Android's `main.c` and iOS's `kivyforge_bootstrap.m` both
2078
+ now run the entry module via `runpy.run_module(entry_point,
2079
+ run_name="__main__", alter_sys=True)`. Credit to
2080
+ [PR #1](https://github.com/ElliotGarbus/kivyforge/pull/1) (kengoon), which
2081
+ independently found and fixed the iOS half of this first.
2082
+
2083
+ ### Desktop apps start faster: the embedded stdlib ships compiled
2084
+
2085
+ - **The staged Python stdlib is byte-compiled at build time** on Windows, Linux
2086
+ and macOS. Measured on a packaged `dice-roller`: `import kivy` costs **66 ms
2087
+ instead of 276 ms** — about 210 ms off every launch. Bundles previously
2088
+ shipped the stdlib as source and re-parsed it on each start, which neither the
2089
+ `.AppImage` (read-only) nor the signed `.app` (writing to itself invalidated
2090
+ its signature) could ever cache away.
2091
+ - **Stdlib sources are kept**, unlike the app payload: they are what tracebacks,
2092
+ `inspect` and `linecache` read. `strip_source` still applies only to your app
2093
+ and its site-packages.
2094
+ - Follows the existing `[tool.kivy.<platform>.build_settings].byte_compile`
2095
+ setting, so it applies to `package` by default and to every build when that is
2096
+ `true`. No new option.
2097
+ - The byte-compile subprocess no longer seeds the artifact with an arbitrary
2098
+ handful of its own import caches, so that subtree is reproducible.
2099
+
2100
+ ### Spawned tools can no longer wait for input
2101
+
2102
+ - **Every external tool kivyforge runs is given a closed stdin.** A child that
2103
+ unexpectedly prompts now gets EOF and fails fast, instead of blocking until a
2104
+ CI job times out with nothing explaining why.
2105
+ - **pip is passed `--no-input`** at the lock resolvers and the wheel staging
2106
+ installs: an index that wants credentials is the one case here that genuinely
2107
+ prompts.
2108
+ - **`kivyforge run` is deliberately unchanged** — the app it launches *is* your
2109
+ program and keeps the user's stdin.
2110
+
2111
+ ### Linux aarch64 is a Raspberry Pi *target*
2112
+
2113
+ - **`[tool.kivy.linux].archs` accepts `"aarch64"`** (opt-in; the default stays
2114
+ `x86_64`). Cross-build on a Linux x86_64 host; run the AppImage on a 64-bit
2115
+ Raspberry Pi OS image (Pi 4 or 5). kivyforge never runs on the Pi.
2116
+ **Verified 2026-09-17 on a Pi 5** (Debian 13, labwc/Wayland, Broadcom V3D);
2117
+ a Pi 4 has not been run.
2118
+ - **`appimagetool` is the host-arch binary**; the aarch64 type2 runtime is
2119
+ embedded with `--runtime-file`. 32-bit ARM and musl stay out of scope.
2120
+
2121
+ ### New: `kivyforge capabilities`
2122
+
2123
+ - **A verb that answers what this kivyforge can do**, with no project, lock or
2124
+ network needed — so an agent can ask before it has anything to point at.
2125
+ `--json` publishes:
2126
+ - `platforms[]`: each target's `archs`, `package_formats`,
2127
+ `default_package_format`, aliases, the host it is the default target for,
2128
+ and **`hosts`: which of macOS/Linux/Windows can build it at all**. That last
2129
+ one is the fact that stops an agent attempting iOS on Windows, and nothing
2130
+ in `--help` states it.
2131
+ - `verbs[]`: every registered verb and whether it accepts `--json`.
2132
+ - `exit_codes` and `diagnostic_codes`: the reserved exit taxonomy and the
2133
+ published `KF-*` vocabulary.
2134
+ - Everything is derived rather than restated: the host matrix comes from asking
2135
+ each backend's own `check_host_capability`, the verb list from the click
2136
+ group, the codes from the modules that define them. A hand-kept copy would
2137
+ pass its tests right up until it went stale.
2138
+
2139
+ ### `--json` on `clean`, `init`, `upgrade` and `open`
2140
+
2141
+ - **Every verb but `run` now takes `--json` and `--no-color`.** Each reports what
2142
+ it did rather than that it did something:
2143
+ - `clean`: `data.removed` lists the generated trees actually deleted (relative,
2144
+ posix), plus `cache_flushed` and `gradle_caches`. Recorded as they go, so a
2145
+ removal that fails part way still names what it had removed.
2146
+ - `init`: `data.action` (`added` / `regenerated`), `data.tables` — the
2147
+ `[tool.kivy*]` tables written — and `data.pyproject`.
2148
+ - `upgrade`: `data.refreshed` names each re-fetched artifact and `data.skipped`
2149
+ counts vendored (path-pinned) entries. A hash mismatch half way through still
2150
+ reports the artifacts already refreshed.
2151
+ - `open`: `data.project` is the project that was opened.
2152
+ - **Two new diagnostic codes**, both warnings on an `ok: true` run:
2153
+ `KF-IDE-NOT-FOUND` when `open` finds no Android Studio launcher on PATH (the
2154
+ project is still generated, just not launched), and `KF-DEPENDENCY-DRIFT` when
2155
+ `init` sees an installed dependency outside its declared specifier.
2156
+ - **`open -p ios` now prints the project it opened**, matching what Android has
2157
+ always printed. It used to succeed silently.
2158
+ - As with `build`/`package`, progress moves to stderr on these verbs: `clean`'s
2159
+ "Stopped the project's Gradle daemon(s)." and `upgrade`'s per-artifact
2160
+ "Refreshing …" lines. What each verb *produced* stays on stdout.
2161
+
2162
+ ### Breaking: `build` and `package` progress moves to stderr
2163
+
2164
+ - **For `build` and `package`, everything except the product lines now goes to
2165
+ stderr**, on all five platforms: staging and signing progress, Android's
2166
+ `[collect]`/`[stage]`/`[gradle]`/`[policy]` lines, and Gradle's own output
2167
+ (also for Android `run` and `--smoke`). stdout keeps only what was produced
2168
+ (`Built …`, `Generated …`, `Packaged …`, `Exported …`) and the distribution
2169
+ advice. On a terminal nothing looks different: the text and its order are
2170
+ unchanged.
2171
+ - **Migration:** a job that captures only stdout
2172
+ (`kivyforge package -p android > build.log`) no longer gets the build log.
2173
+ Capture both streams (`> build.log 2>&1`).
2174
+ - **When `xcodebuild` or `appimagetool` fails**, its output is printed as
2175
+ progress and the error is a one-line summary (`xcodebuild archive failed
2176
+ (exit 65); its output is above.`) instead of embedding the whole log.
2177
+ `appimagetool` failures now show both of its streams; stdout used to be
2178
+ dropped whenever stderr had anything in it.
2179
+
2180
+ ### `build --json` and `package --json`
2181
+
2182
+ - Both verbs take `--json` and `--no-color`, like the other verbs. The envelope's
2183
+ `data.artifacts` lists what this run produced, as `{"path", "kind"}` with the
2184
+ path relative to the project root and `kind` one of `appimage`, `apk`, `aab`,
2185
+ `app`, `ipa`, `folder`, `project`.
2186
+ - Only products this invocation finalised are listed, including on failure: a
2187
+ `build` that fails after generating the project still names the project; a
2188
+ `package` that fails names nothing, never a previous run's artifact.
2189
+ - Success-path warnings arrive as diagnostics on an `ok: true` envelope:
2190
+ `KF-SIGNING-UNCONFIGURED`, `KF-BYTECOMPILE-NO-INTERP`, `KF-MANIFEST-POLICY`
2191
+ and `KF-ENTITLEMENTS-UNGRANTED`.
2192
+ - **Failures now say what kind of failure they are.** `build` and `package` used
2193
+ to exit `1` for everything; they now use the reserved exit codes, with a
2194
+ matching diagnostic code:
2195
+
2196
+ | Exit | Code | When |
2197
+ |---|---|---|
2198
+ | `3` | `KF-HOST-INCAPABLE` | wrong host OS for the target |
2199
+ | `3` | `KF-TOOLCHAIN-MISSING` | a tool is not installed (`xcodebuild`, the Gradle wrapper, `clang`, `codesign`, `signtool`, `appimagetool`, …) |
2200
+ | `3` | `KF-TOOLCHAIN-UNUSABLE` | a tool is present but cannot be executed; `context.errno` says why |
2201
+ | `4` | `KF-LOCK-MISSING` / `KF-LOCK-UNREADABLE` / `KF-LOCK-DRIFT` | re-run `kivyforge lock` |
2202
+ | `5` | `KF-BUILD-TOOL-FAILED` | Gradle, `xcodebuild` or `appimagetool` exited non-zero; `context` has `tool` and `task` |
2203
+ | `5` | `KF-ARTIFACT-MISSING` | the tool reported success but the product is not there |
2204
+
2205
+ Anything not listed keeps `KF-ERROR` and exit `1`. A tool that cannot be
2206
+ started at all used to crash with a traceback on iOS and Android; it now
2207
+ fails cleanly with an envelope.
2208
+ - Android now prints `Generated <app>-android` and relative artifact paths, like
2209
+ the other platforms. iOS `build --simulator`/`--device` builds into the
2210
+ project's own `build/DerivedData` (as `run` already did) and prints the
2211
+ `.app` it produced.
2212
+
2213
+ ### Breaking: Apple targets are Apple Silicon only
2214
+
2215
+ - **`x86_64` removed from macOS and the iOS Simulator**, as a host *and* as a
2216
+ target. macOS 27 stops installing on Intel hardware, GitHub's `macos-*-intel`
2217
+ runners sunset in 2027, and macOS 28 removes Rosetta — so an Intel slice is
2218
+ one nothing can execute or validate. Legacy tools continue to cover Intel;
2219
+ a new toolchain does not need to carry it.
2220
+ - `[tool.kivy.macos].archs` accepts only `["arm64"]` (the new default), and
2221
+ `[tool.kivy.ios].simulator_archs` only `["arm64"]`. Both reject `x86_64`
2222
+ with a migration message naming the fix.
2223
+ - **universal2 builds are gone** along with the `lipo` merge machinery, and
2224
+ `--arch universal2` is no longer a CLI choice.
2225
+ - iOS locks now pin **two** slices (device arm64 + simulator arm64) instead
2226
+ of three.
2227
+ - **Migration:** set `archs = ["arm64"]` / `simulator_archs = ["arm64"]` (or
2228
+ drop the keys — arm64 is the default) and re-lock.
2229
+ - **`universal2` *wheels* are unaffected** and still resolve normally — a
2230
+ universal2 wheel contains arm64. The *build target* set narrowed; the
2231
+ *wheel tag* set did not.
2232
+
2233
+ ### Desktop source stripping (Windows/Linux/macOS)
2234
+
2235
+ - **New `[tool.kivy.<platform>.build_settings]` table** on Windows, Linux, and
2236
+ macOS, matching what Android's own `build_settings` already does:
2237
+ `byte_compile` and `strip_source` (each `"release"` | `true` | `false`,
2238
+ default `"release"`). `"release"` applies only to `kivyforge package` —
2239
+ `build`/`run` always keep readable `.py` for fast iteration.
2240
+ - Only the app sources and installed site-packages are compiled/stripped;
2241
+ the staged Python stdlib is never touched (readable tracebacks,
2242
+ `inspect`/`linecache` stay intact).
2243
+ - The compiler is the bundle's own staged interpreter when it can run on
2244
+ the host (never Rosetta or any other emulation), else the interpreter
2245
+ running `kivyforge` itself on a matching CPython minor, else the default
2246
+ degrades to shipping source with a note (`byte_compile = true` fails the
2247
+ build instead of silently shipping source).
2248
+ - On macOS this runs before ad-hoc signing, since codesign seals the
2249
+ bundle.
2250
+ - Previously only PyInstaller (bytecode-only) and Flet (bytecode-only by
2251
+ default since v0.86) did this; kivyforge's desktop backends shipped raw
2252
+ `.py` unconditionally until now.
2253
+
2254
+ ### iOS source stripping
2255
+
2256
+ - **New `[tool.kivy.ios.python.build_settings]` table** (nested under
2257
+ `python`, since `[tool.kivy.ios].build_settings` already names the Xcode
2258
+ passthrough table): the same `byte_compile`/`strip_source` release
2259
+ tri-state as Android and the desktop backends, covering both `pip-deps`
2260
+ (third-party wheels) and the app's own source.
2261
+ - `build`/`run` are unaffected — `app/` stays a symlink to `app_dir` for
2262
+ fast iteration, exactly as before.
2263
+ - `kivyforge package` now stages `app/` as a real, disposable copy instead
2264
+ of a symlink and compiles/strips *that* — the user's actual source tree
2265
+ is never touched. Switching back to `build`/`run` afterward reclaims the
2266
+ copy for the symlink automatically.
2267
+ - The staged `Python.xcframework` stdlib is never touched (it's a
2268
+ prebuilt, opaque library either way).
2269
+ - `Python.xcframework` ships no standalone interpreter to shell out to, so
2270
+ compilation always uses the `kivyforge`-hosting interpreter itself (on a
2271
+ matching CPython minor) or degrades — there is no "staged interpreter"
2272
+ rung the way desktop has.
2273
+
2274
+ ### Android backend (`.apk` / `.aab`)
2275
+
2276
+ - **New `android` target.** `kivyforge lock/build/run/package -p android`
2277
+ resolves into `pylock.android.toml`, assembles the official
2278
+ [python.org Android runtime](https://www.python.org/downloads/android/) (per
2279
+ ABI) plus per-ABI `android_*` wheels into a generated **Gradle/AGP** project,
2280
+ and produces a signed **`.apk`** (sideload/CI) or **`.aab`** (Play upload). A
2281
+ **wheel-assembly + Gradle-drive** backend — no python-for-android recipe
2282
+ system, no per-app C-extension compilation. Builds on Windows, macOS, or Linux
2283
+ (consumes prebuilt wheels + runtime; drives the SDK/NDK/Gradle). 64-bit only
2284
+ (`arm64_v8a`, `x86_64` — the python.org runtime ships no 32-bit build).
2285
+ - **`[tool.kivy.android]` overlay** — `package` (applicationId), `version_code`
2286
+ (explicit or `"auto"` derived from `[project].version`), `min_sdk`/`target_sdk`/
2287
+ `compile_sdk`, `kivy_generation` (2 = Kivy 2.3.1, 3 = Kivy 3.0), `abis`, adaptive
2288
+ `icons`/`splash` (AndroidX SplashScreen), `permissions` (+ implied
2289
+ `<uses-feature>` synthesis), `services`/`activities`/`intent_filters`, the
2290
+ manifest passthrough (+ raw-XML), `native.aars`/`.jars` and Maven `gradle`
2291
+ dependencies, `include_files`, `src` (Java/Kotlin), `signing` (v1–v4 schemes),
2292
+ and `build_settings` (byte-compile/strip/R8/debug-symbols). Loader-validated
2293
+ (rules 1–21).
2294
+ - **kivyforge-owned bootstrap** — a generated `org.kivy.android.PythonActivity`
2295
+ (extends stock SDL2 `SDLActivity`), the pyjnius `NativeInvocationHandler`
2296
+ matched pair (with a hard `invoke0` contract gate), the Kivy-compat shims
2297
+ (`org.renpy.android.Hardware` + `mActivity`), an `SDL_main` native launcher
2298
+ (NDK-compiled per ABI), and a `sys.meta_path` finder over flattened extension
2299
+ `.so`s in `jniLibs/`. **pyjnius ships as a prebuilt Java-free wheel** (SDL-
2300
+ agnostic, `JNIEnv` resolved at runtime).
2301
+ - **Four artifact channels** — `android_*` wheels (PyPI / supplemental indexes /
2302
+ vendored `find_links`), the python.org runtime, `.aar`/`.jar` archives, and
2303
+ Maven coordinates (Gradle resolves; the graph is SHA-256-recorded in the lock).
2304
+ - **Mandatory signing** — debug builds use the auto-managed debug keystore;
2305
+ `package` requires a release keystore (env-var passwords, never in
2306
+ `pyproject.toml`), with a signing pre-flight and a release manifest-policy
2307
+ pre-flight (exported components, `debuggable`, placeholder package, …) that
2308
+ fail before Gradle.
2309
+ - **`kivyforge run --smoke`** — a generated instrumented contract test validates
2310
+ the extension-module finder + the pyjnius `invoke0` glue on a real
2311
+ device/emulator; the promotion mechanism for the compatibility matrix.
2312
+ - **`kivyforge doctor -p android`** — JDK/SDK/build-tools/NDK/emulator/adb, plus
2313
+ project checks (SDL↔Kivy, the pyjnius contract, a hermetic **16 KB
2314
+ ELF-alignment scanner**, ABI coverage, signing, lock-host reachability).
2315
+ - **Examples** — `hello-android` (Kivy 2.3.1 / SDL2), `hello-sdl3` (Kivy 3.0 /
2316
+ SDL3), `pyjnius-deviceinfo` (device info via pyjnius), and `qr-maven`
2317
+ (Google ZXing via the Maven channel + a Python-implements-Java `invoke0`
2318
+ round-trip), plus `examples/verify-android.ps1`.
2319
+ - **First-party wheels, no vendoring.** Kivy (2.3.1 and 3.0), pyjnius, and the
2320
+ matching SDL family are resolved from the
2321
+ [kivy-mobile-wheels](https://github.com/ElliotGarbus/kivy-mobile-wheels)
2322
+ index — a companion repo of first-party cibuildwheel cross-builds (official
2323
+ SDL sources, 16 KB page-aligned, sonames unchanged for `System.loadLibrary`)
2324
+ published as a static PEP 503 index. `hello-android` and `hello-sdl3` carry
2325
+ their locks committed as validation evidence; the rest resolve like any
2326
+ other dependency.
2327
+ - **SDL3 / Kivy 3.0 support.** `kivy_generation = 3` renders the SDL3 bootstrap template
2328
+ (`templates/sdl3/`) and pairs it with the matching wheel-repo glue;
2329
+ `check_sdl_glue_contract()` fails the build on any Java/`.so` version
2330
+ mismatch rather than the silent black-screen failure that mismatch would
2331
+ otherwise produce.
2332
+ - **Validated on real hardware.** `kivyforge run --smoke` passes on an x86_64
2333
+ emulator (API 31) **and** a Pixel 8a (Android 16 / API 36) for both the
2334
+ SDL2 and SDL3 stacks, with the app visually confirmed rendering on-device —
2335
+ see the [compatibility matrix](docs/design/platforms/android/08-compatibility-matrix.md).
2336
+
2337
+ ### Windows backend (onedir folder)
2338
+
2339
+ - **New `windows` target.** `kivyforge lock/build/run/package -p windows`
2340
+ resolves into `pylock.windows.toml`, bundles a relocatable
2341
+ [python-build-standalone](https://github.com/astral-sh/python-build-standalone)
2342
+ MSVC CPython plus `win_amd64` wheels, and produces a run-from-folder **`onedir`**:
2343
+ a windowed-subsystem launcher `.exe` beside the full runtime prefix, the app
2344
+ source, a generated bootstrap, and the native-binaries channel. amd64 only this
2345
+ phase; the config stays list-shaped for additive `win-arm64` later. Requires a
2346
+ Windows host (MSVC runtime + Windows-only rcedit/signtool).
2347
+ - **`[tool.kivy.windows]` overlay** — `app_id` (the AppUserModelID; hard
2348
+ constraints enforced at config time, PascalCase/period style a doctor WARN),
2349
+ `archs`, `python.version`, `icons.source`, optional `[signing]`
2350
+ (`thumbprint`/`timestamp_url`/`store_scope`), and the shared
2351
+ `extra_index_urls`/`find_links`/`exclude`.
2352
+ - **Prebuilt, reproducible launcher** — a single vendored windowed launcher
2353
+ `.exe` (built once from `launcher.c` with MSVC `/Brepro` + `/RELEASE` +
2354
+ `/EMITTOOLVERSIONINFO:NO`, SHA-256 pinned, byte-reproducibility gated in CI —
2355
+ the last flag drops the PE "rich header", whose serviced `cl`/`link` build
2356
+ stamp otherwise differs between runner images sharing one `VCTOOLSVERSION`) is
2357
+ parameterized per app only by its *resources* (icon + version) via the
2358
+ vendored `rcedit` — no per-build compiler.
2359
+ The launcher self-locates, sets the AppUserModelID, hands the console off on
2360
+ `run` while suppressing the flash on double-click, and spawns the bundled
2361
+ `python.exe` under a Job object.
2362
+ - **No-pip wheel staging** — locked wheels are unpacked into the runtime prefix
2363
+ by a deterministic wheel-scheme installer (routing `purelib`/`platlib`/`data`/
2364
+ `scripts`/`headers`), and `kivy_deps.*` DLLs land where the loader finds them.
2365
+ The `vcruntime140*.dll` are staged from the PBS prefix (empirically `msvcp140`
2366
+ is handled best-effort).
2367
+ - **Native-binaries channel + PE checks** — the shared staging helper gained
2368
+ Windows-facing guards (case-insensitive collision keys, reserved DOS
2369
+ device-name / alternate-data-stream rejection, exec-bit no-op) and a
2370
+ pure-Python PE machine-type reader so a wrong-architecture DLL fails the build
2371
+ clearly. The generated bootstrap registers `bin\` with `os.add_dll_directory`
2372
+ and prepends it to `PATH` (helpers run by name, DLLs load by name).
2373
+ - **Unsigned folder packaging** — `package -f folder` atomically copies the
2374
+ onedir into `dist\windows\<app>-<ver>-amd64\`, excluding build cache / VCS /
2375
+ editor droppings; the build tree stays the unsigned dev-run target.
2376
+ - **Optional Authenticode signing** — first-class but off by default. A new
2377
+ `Signer` protocol with `SigntoolSigner` (`signtool sign /sha1 <thumbprint>
2378
+ /fd SHA256 /tr <url> /td SHA256`, `/sm` for `store_scope=machine`, always
2379
+ timestamped) and `NullSigner`; identity is always a cert-store thumbprint,
2380
+ never a `.pfx`. A deterministic PEP 440 → four-part numeric `FILEVERSION`
2381
+ mapping feeds the version resource. Signing targets the `dist` copy only,
2382
+ after the resource patch.
2383
+ - **`kivyforge doctor -p windows`** — Windows host, long-path support,
2384
+ `app_id` style, arch coverage, app icon, native-binary sources/collisions/arch,
2385
+ resource assets, reachable hosts (incl. the signing timestamp server),
2386
+ `signtool` availability, and the signing certificate (in the configured
2387
+ `store_scope` store).
2388
+ - **Examples** — `desktop-viewer`, `dice-roller`, `notes`, and `hello-native`
2389
+ gained `[tool.kivy.windows]` overlays (hello-native adds a `greet.dll` +
2390
+ `roll.exe` native channel); `examples/verify-windows-examples.ps1` mirrors the
2391
+ desktop verifier's clean→lock→doctor→build→run→package→launch loop.
2392
+ - **CI** — `windows-latest` unit tests, a launcher reproducibility + vendored-asset
2393
+ gate, and a self-signed `signtool` sign→verify loop.
2394
+
2395
+ ### Linux backend (AppImage / AppDir)
2396
+
2397
+ - **New `linux` target.** `kivyforge lock/build/run/package -p linux` resolves
2398
+ into `pylock.linux.toml`, bundles a relocatable
2399
+ [python-build-standalone](https://github.com/astral-sh/python-build-standalone)
2400
+ gnu/glibc CPython plus manylinux wheels, and produces an
2401
+ [AppImage](https://appimage.org/) (the `package` default) with a
2402
+ run-from-folder **AppDir** as the substrate/fallback (`package -f folder`).
2403
+ x86_64 only this phase; the config stays list-shaped for additive `aarch64`
2404
+ later.
2405
+ - **`[tool.kivy.linux]` overlay** — `app_id` (reverse-DNS; names the generated
2406
+ `.desktop`, `Icon=`, and `StartupWMClass`), `archs`, optional `glibc_floor`,
2407
+ `python.version`, `icons.source`, `desktop.categories`, and the shared
2408
+ `extra_index_urls`/`find_links`/`exclude`. Invalid `app_id` characters are a
2409
+ fail-fast config error.
2410
+ - **manylinux resolution** — because pip does not expand the manylinux hierarchy
2411
+ from a single `--platform`, the resolver requests the full compatible tag
2412
+ ladder at/below the glibc floor in one invocation. The artifact's *effective*
2413
+ glibc floor = `max(runtime floor 2.17, highest locked wheel manylinux level)`.
2414
+ - **AppImage packaging** — a pinned static `appimagetool` + pinned type2
2415
+ static-FUSE runtime are fetched through the shared artifact cache; the shipped
2416
+ AppImage needs no host `libfuse2`, and `APPIMAGE_EXTRACT_AND_RUN=1` means the
2417
+ build host needs no FUSE either. A generated `AppRun` (POSIX shell) sets
2418
+ `PYTHONHOME`/`PYTHONPATH`/`SDL_VIDEO_*_WMCLASS` — no compiled launcher, no
2419
+ native build at package time.
2420
+ - **Generated desktop integration** — `.desktop` entry + hicolor icon set from
2421
+ `pyproject.toml` (Pillow for resize via the optional `kivyforge[linux]` extra;
2422
+ a default icon is generated with only the stdlib when no source is set).
2423
+ - **`kivyforge doctor -p linux`** — host libc (WARN on musl), `libGL`/`libEGL`
2424
+ via `ldconfig`, display session, glibc floor vs. runtime, manylinux arch
2425
+ coverage, app icon, generated `.desktop` validity (`desktop-file-validate`),
2426
+ and reachable hosts (lock + appimagetool/runtime assets).
2427
+ - **Host contract (documented, never vendored)** — the bundle relies on the host
2428
+ for glibc ≥ the effective floor, `libGL`/`libEGL`, and an X11/Wayland session.
2429
+ - **Examples** — `desktop-viewer`, `dice-roller`, and `notes` gained
2430
+ `[tool.kivy.linux]` overlays + committed `pylock.linux.toml`;
2431
+ `examples/run-examples.sh -p linux` builds and runs them.
2432
+ - **CI** — added an `ubuntu-latest` headless job (the cross-platform core now
2433
+ runs on both macOS and Linux hosts). Artifact cache respects
2434
+ `$XDG_CACHE_HOME` on Linux (`~/.cache/kivyforge`).
2435
+
2436
+ ## The rewrite (never released as 3.0.0)
2437
+
2438
+ **Complete rewrite.** kivyforge is the successor to kivy-ios, python-for-android,
2439
+ and buildozer — a declarative, PEP 621-aligned build toolchain for Kivy apps
2440
+ aimed at every platform Kivy runs on. It began with the **iOS** target, built
2441
+ around pre-built Python.xcframeworks and pip-installable iOS wheels.
2442
+ The recipe-based `kivyforge build-recipe` workflow is gone; see the
2443
+ [design docs](docs/design/common/00-overview.md) and the updated README.
2444
+
2445
+ ### What the rewrite introduced
2446
+
2447
+ - **Declarative config** — project settings live in `[tool.kivy]` /
2448
+ `[tool.kivy.ios]` sections of your app's `pyproject.toml`; no more
2449
+ `toolchain.spec` or `requirements.txt`. Config validation is strict and
2450
+ fail-fast: `[tool.kivy.ios.python].version` is required (no hidden default at
2451
+ lock time), native xcframework `source` paths may not escape the project,
2452
+ signing flags must be real booleans (a quoted `"false"` is rejected, not
2453
+ coerced to `true`), and `[tool.kivy.ios.xcode.build_settings]` values must be
2454
+ strings.
2455
+ - **Dependency locking** — `kivyforge lock` resolves pure-Python and iOS wheel
2456
+ dependencies and writes `pylock.ios.toml` (PEP 751-inspired).
2457
+ - **Python.xcframework** — Python is distributed as a pre-built xcframework;
2458
+ no source compilation required on the developer machine.
2459
+ - **iOS wheels** — third-party packages are distributed as pre-built
2460
+ `*-ios.whl` wheels; `kivyforge build` downloads and integrates them.
2461
+ - **Xcode project generation** — `kivyforge init` generates a complete
2462
+ `.xcodeproj` via `pbxproj`; `kivyforge open` launches Xcode.
2463
+ - **SDL3** — Kivy 3.0 moves to SDL3; the generated project and Info.plist
2464
+ are configured for the SDL3 UIScene lifecycle out of the box.
2465
+ - **Mobile window/display geometry via `kivy.mobile`** — DPI, scale, safe-area
2466
+ insets, and keyboard height are provided by Kivy core's `kivy.mobile` module
2467
+ (kivy/kivy#9331), shipped in the Kivy iOS wheel.
2468
+ - **Native iOS dependencies** — declare Swift Package Manager packages and
2469
+ native `.xcframework` archives under `[tool.kivy.ios.native]`. `kivyforge lock`
2470
+ pins SPM packages to a concrete revision and resolves each xcframework archive
2471
+ (by URL or repo-relative path) into a SHA-256 + enumerated slice list in
2472
+ `pylock.ios.toml`; `kivyforge build` fetches, verifies, and wires them into the
2473
+ Xcode project's Link / Embed phases per each entry's `link`/`embed` intent,
2474
+ pruning references for frameworks no longer present. When two sources stage the
2475
+ same `<name>.xcframework`, identical content (matching tree hash) is
2476
+ deduplicated silently and conflicting content fails the build, naming both
2477
+ providers and their hashes.
2478
+ - **Slim CLI** — `toolchain` now dispatches to `init`, `lock`, `build`, `run`,
2479
+ `open`, `upgrade`, `clean`, `status`, and `doctor`; legacy 2.x verbs print
2480
+ a migration pointer.
2481
+ - **Requires Python ≥ 3.13 and pip ≥ 24.3** on the developer machine (macOS
2482
+ only). pip 24.3 added PEP 730 iOS platform-tag matching, which `toolchain
2483
+ lock` relies on to resolve iOS wheels; `kivyforge doctor` flags older pip and
2484
+ `kivyforge lock` fails fast with an upgrade hint.
2485
+
2486
+ ---
2487
+
2488
+ > **Historical entries below this line describe kivy-ios 2.x** (the
2489
+ > recipe-based toolchain). They are preserved for reference but describe
2490
+ > a workflow that no longer exists in kivyforge.
2491
+
2492
+ ---
2493
+
2494
+ ## [v2025.05.17](https://github.com/kivy/kivy-ios/tree/v2025.05.17)
2495
+
2496
+ [Full Changelog](https://github.com/kivy/kivy-ios/compare/v2024.03.17...v2025.05.17)
2497
+
2498
+ **Closed issues:**
2499
+
2500
+ - kivy fails to build on MacOS 15.4.1 \(-armv7 compiler failure\) [\#965](https://github.com/kivy/kivy-ios/issues/965)
2501
+ - push notification in ios? [\#964](https://github.com/kivy/kivy-ios/issues/964)
2502
+ - Libffi error while building python3 with Xcode 16.3 [\#963](https://github.com/kivy/kivy-ios/issues/963)
2503
+ - Fails to build on recipe for ios/src/ios.pyx [\#948](https://github.com/kivy/kivy-ios/issues/948)
2504
+ - target python needs to be patched [\#939](https://github.com/kivy/kivy-ios/issues/939)
2505
+ - Toolchain build kivy command is failing with Xcode 16 [\#936](https://github.com/kivy/kivy-ios/issues/936)
2506
+ - Toolchain does not build with Xcode 15.4 [\#929](https://github.com/kivy/kivy-ios/issues/929)
2507
+ - How is Accelerate not supported? [\#925](https://github.com/kivy/kivy-ios/issues/925)
2508
+ - M1 mac python3 build error [\#924](https://github.com/kivy/kivy-ios/issues/924)
2509
+ - Python error after building to iPhone Simulator [\#923](https://github.com/kivy/kivy-ios/issues/923)
2510
+ - Pillow not building [\#907](https://github.com/kivy/kivy-ios/issues/907)
2511
+ - Can't create a recipe [\#906](https://github.com/kivy/kivy-ios/issues/906)
2512
+ - Linker Command Failure Error While Building Python3 [\#905](https://github.com/kivy/kivy-ios/issues/905)
2513
+ - MacOS kivyforge build constantly failing \(I think due to C extension errors\) [\#901](https://github.com/kivy/kivy-ios/issues/901)
2514
+ - Unable to build with kivymd==2.0 [\#897](https://github.com/kivy/kivy-ios/issues/897)
2515
+ - Got dlopen error on Foundation [\#896](https://github.com/kivy/kivy-ios/issues/896)
2516
+ - Feature request: MatplotLib for iOS [\#712](https://github.com/kivy/kivy-ios/issues/712)
2517
+
2518
+ **Merged pull requests:**
2519
+
2520
+ - Use `-platform_version` flag during linking instead of the deprecated and now removed\) `-ios_simulator_version_min` [\#970](https://github.com/kivy/kivy-ios/pull/970) ([misl6](https://github.com/misl6))
2521
+ - Bump `libpng` version to `1.6.48` [\#967](https://github.com/kivy/kivy-ios/pull/967) ([misl6](https://github.com/misl6))
2522
+ - Disable iOS armv7 build for `libffi` [\#966](https://github.com/kivy/kivy-ios/pull/966) ([misl6](https://github.com/misl6))
2523
+ - Add `SDL3`, `SDL3_ttf`, `SDL3_mixer`, `SDL3_image` recipes for Kivy `3.0.0` [\#961](https://github.com/kivy/kivy-ios/pull/961) ([misl6](https://github.com/misl6))
2524
+ - chore\(deps\): update pypa/gh-action-pypi-publish action to v1.12.4 [\#958](https://github.com/kivy/kivy-ios/pull/958) ([renovate[bot]](https://github.com/apps/renovate))
2525
+ - Enhance Cython integration: Add support for `c++` mode, use hostpython3-provided Cython, allow pinning of specific Cython version in recipe [\#957](https://github.com/kivy/kivy-ios/pull/957) ([misl6](https://github.com/misl6))
2526
+ - Bump Kivy version to 2.3.1 [\#956](https://github.com/kivy/kivy-ios/pull/956) ([misl6](https://github.com/misl6))
2527
+ - chore\(deps\): update pypa/gh-action-pypi-publish action to v1.12.3 [\#952](https://github.com/kivy/kivy-ios/pull/952) ([renovate[bot]](https://github.com/apps/renovate))
2528
+ - chore\(deps\): update pypa/gh-action-pypi-publish action to v1.12.2 [\#942](https://github.com/kivy/kivy-ios/pull/942) ([renovate[bot]](https://github.com/apps/renovate))
2529
+ - Fix libb2 issue [\#940](https://github.com/kivy/kivy-ios/pull/940) ([tcaduser](https://github.com/tcaduser))
2530
+ - :construction\_worker: Fix broken setup workflow [\#938](https://github.com/kivy/kivy-ios/pull/938) ([AndreMiras](https://github.com/AndreMiras))
2531
+ - fix build for Xcode 16 [\#937](https://github.com/kivy/kivy-ios/pull/937) ([tcaduser](https://github.com/tcaduser))
2532
+ - Fix zbarlight recipe [\#932](https://github.com/kivy/kivy-ios/pull/932) ([Cheaterman](https://github.com/Cheaterman))
2533
+ - chore\(deps\): update actions/setup-python action to v5 [\#918](https://github.com/kivy/kivy-ios/pull/918) ([renovate[bot]](https://github.com/apps/renovate))
2534
+ - chore\(deps\): update pypa/gh-action-pypi-publish action to v1.10.2 [\#917](https://github.com/kivy/kivy-ios/pull/917) ([renovate[bot]](https://github.com/apps/renovate))
2535
+ - Bump minimum Python supported version to `3.8` [\#914](https://github.com/kivy/kivy-ios/pull/914) ([misl6](https://github.com/misl6))
2536
+ - chore: Configure Renovate [\#910](https://github.com/kivy/kivy-ios/pull/910) ([renovate[bot]](https://github.com/apps/renovate))
2537
+ - Sunset `apple-silicon-m1` self-hosted runner, as now is supported by Github Hosted runners via `macos-latest` tag. Use `macos-13` for runs on Intel macs [\#909](https://github.com/kivy/kivy-ios/pull/909) ([misl6](https://github.com/misl6))
2538
+ - \(`recipes/materialyoucolor`\): update to 2.0.9 [\#904](https://github.com/kivy/kivy-ios/pull/904) ([T-Dynamos](https://github.com/T-Dynamos))
2539
+
2540
+ ## [v2024.03.17](https://github.com/kivy/kivy-ios/tree/v2024.03.17)
2541
+
2542
+ [Full Changelog](https://github.com/kivy/kivy-ios/compare/v2023.08.24...v2024.03.17)
2543
+
2544
+ **Fixed bugs:**
2545
+
2546
+ - zbarlite recipe has hardcoded python version [\#677](https://github.com/kivy/kivy-ios/issues/677)
2547
+
2548
+ **Closed issues:**
2549
+
2550
+ - Materialyoucolor-python Recipe [\#895](https://github.com/kivy/kivy-ios/issues/895)
2551
+ - libffi recipe build hangs during configuration [\#889](https://github.com/kivy/kivy-ios/issues/889)
2552
+ - Can't Input text on IOS17.2 Simulator with Xcode 15.1 [\#888](https://github.com/kivy/kivy-ios/issues/888)
2553
+ - \[Request\]: Pypi packages [\#886](https://github.com/kivy/kivy-ios/issues/886)
2554
+ - Add pdf2docx recipe [\#883](https://github.com/kivy/kivy-ios/issues/883)
2555
+ - \[Question\] : Pygame and pyexpat ? [\#873](https://github.com/kivy/kivy-ios/issues/873)
2556
+ - Support mail address is broken [\#872](https://github.com/kivy/kivy-ios/issues/872)
2557
+ - Toolchain python version mismatch [\#869](https://github.com/kivy/kivy-ios/issues/869)
2558
+ - how to show ip camera footage [\#868](https://github.com/kivy/kivy-ios/issues/868)
2559
+ - Working app crash on iOS 17 [\#864](https://github.com/kivy/kivy-ios/issues/864)
2560
+ - Crash during background execution [\#862](https://github.com/kivy/kivy-ios/issues/862)
2561
+ - Python3 build fails with `unknown argument: '-fp-model'` [\#860](https://github.com/kivy/kivy-ios/issues/860)
2562
+ - issue creating recipe for kiwsolver [\#857](https://github.com/kivy/kivy-ios/issues/857)
2563
+ - App runs fine from XCode, but terminates with an error if installed through TestFlight [\#850](https://github.com/kivy/kivy-ios/issues/850)
2564
+ - ffmpeg related errors after adding this recipe to the project [\#849](https://github.com/kivy/kivy-ios/issues/849)
2565
+ - how do you init your custom c module at app startup? [\#848](https://github.com/kivy/kivy-ios/issues/848)
2566
+ - Replacement for pycrypto [\#843](https://github.com/kivy/kivy-ios/issues/843)
2567
+ - Xcode build fails with ModuleNotFoundError: No module named 'encodings' [\#842](https://github.com/kivy/kivy-ios/issues/842)
2568
+ - Invalid Signature when adding Python to iOS App Extension [\#826](https://github.com/kivy/kivy-ios/issues/826)
2569
+ - APP crashes easily [\#824](https://github.com/kivy/kivy-ios/issues/824)
2570
+ - when creating xcodeproj using kivy-ios, how to change the default bundleIdentifier [\#823](https://github.com/kivy/kivy-ios/issues/823)
2571
+ - kivyforge build: no matching architecture in universal wrapper [\#792](https://github.com/kivy/kivy-ios/issues/792)
2572
+ - Explanation to build recipe in iOS [\#776](https://github.com/kivy/kivy-ios/issues/776)
2573
+ - Support arm64 simulator on Apple Silicon hardware [\#751](https://github.com/kivy/kivy-ios/issues/751)
2574
+ - PyObjC import error [\#741](https://github.com/kivy/kivy-ios/issues/741)
2575
+ - App Store Connect Operation Error Invalid Bundle Structure - The binary file '\*myapp\*.app/lib/python3.9/site-packages/google/protobuf/pyext/\_message.cpython-39-darwin.so' is not permitted. [\#702](https://github.com/kivy/kivy-ios/issues/702)
2576
+ - libzbar recipe missing libiconv dependency [\#676](https://github.com/kivy/kivy-ios/issues/676)
2577
+ - Lib Not Found [\#674](https://github.com/kivy/kivy-ios/issues/674)
2578
+ - kivyforge build kivy Error [\#668](https://github.com/kivy/kivy-ios/issues/668)
2579
+ - leverage conda to create the host\* packages [\#655](https://github.com/kivy/kivy-ios/issues/655)
2580
+ - ImportError: dynamic module does not define module export function \(PyInit\_\_imaging\) [\#644](https://github.com/kivy/kivy-ios/issues/644)
2581
+ - kivy ios and boto3 [\#641](https://github.com/kivy/kivy-ios/issues/641)
2582
+ - Recipe for OpenCV [\#637](https://github.com/kivy/kivy-ios/issues/637)
2583
+ - Issues with using threads on an iOS device [\#609](https://github.com/kivy/kivy-ios/issues/609)
2584
+ - Xcode fails to build: Undefined symbols for architecture x86\_64 [\#607](https://github.com/kivy/kivy-ios/issues/607)
2585
+ - There is a memory leak in the program [\#419](https://github.com/kivy/kivy-ios/issues/419)
2586
+ - Include Unit tests and CI to test build for the toolchain [\#296](https://github.com/kivy/kivy-ios/issues/296)
2587
+
2588
+ **Merged pull requests:**
2589
+
2590
+ - :construction\_worker: Fix the rebuild recipe script [\#900](https://github.com/kivy/kivy-ios/pull/900) ([AndreMiras](https://github.com/AndreMiras))
2591
+ - recipes: add new `materialyoucolor` recipe [\#898](https://github.com/kivy/kivy-ios/pull/898) ([T-Dynamos](https://github.com/T-Dynamos))
2592
+ - Remove bitcode support, as is now deprecated by Apple [\#894](https://github.com/kivy/kivy-ios/pull/894) ([misl6](https://github.com/misl6))
2593
+ - Bump openssl version to `1.1.1w` [\#891](https://github.com/kivy/kivy-ios/pull/891) ([misl6](https://github.com/misl6))
2594
+ - Bump Kivy version to 2.3.0 [\#890](https://github.com/kivy/kivy-ios/pull/890) ([misl6](https://github.com/misl6))
2595
+ - Set `fetch-depth: 0` for updated recipes checkout and use `actions/checkout@v4` instead of `actions/checkout@v3` [\#884](https://github.com/kivy/kivy-ios/pull/884) ([misl6](https://github.com/misl6))
2596
+ - Update sdl2 deps to reflect the same targeted in kivy/kivy [\#881](https://github.com/kivy/kivy-ios/pull/881) ([misl6](https://github.com/misl6))
2597
+ - Make doc structure consistent and up-to-date [\#879](https://github.com/kivy/kivy-ios/pull/879) ([Julian-O](https://github.com/Julian-O))
2598
+ - Create no-response [\#878](https://github.com/kivy/kivy-ios/pull/878) ([Julian-O](https://github.com/Julian-O))
2599
+ - Support Section Updated issue \#872 resolved [\#876](https://github.com/kivy/kivy-ios/pull/876) ([seharbat00l](https://github.com/seharbat00l))
2600
+ - Install kivy-ios via pip during tests, so dependencies are automatically managed [\#871](https://github.com/kivy/kivy-ios/pull/871) ([misl6](https://github.com/misl6))
2601
+ - Update `python3` recipe to `3.11.6` [\#870](https://github.com/kivy/kivy-ios/pull/870) ([misl6](https://github.com/misl6))
2602
+ - Do not use full path for system libraries and frameworks in `.xcodeproj` [\#867](https://github.com/kivy/kivy-ios/pull/867) ([misl6](https://github.com/misl6))
2603
+ - Fix for upstream issues with compiler path [\#866](https://github.com/kivy/kivy-ios/pull/866) ([tcaduser](https://github.com/tcaduser))
2604
+ - update to matplotlib 3.7.2 [\#861](https://github.com/kivy/kivy-ios/pull/861) ([tcaduser](https://github.com/tcaduser))
2605
+ - update for recent iOS platform changes [\#859](https://github.com/kivy/kivy-ios/pull/859) ([tcaduser](https://github.com/tcaduser))
2606
+ - matplotlib recipe [\#858](https://github.com/kivy/kivy-ios/pull/858) ([tcaduser](https://github.com/tcaduser))
2607
+ - Bump `numpy` version to `1.24.4` [\#856](https://github.com/kivy/kivy-ios/pull/856) ([misl6](https://github.com/misl6))
2608
+ - Support ARM64 Simulator + Introduce build platform concept + Introduce `xcframework` [\#778](https://github.com/kivy/kivy-ios/pull/778) ([misl6](https://github.com/misl6))
2609
+
2610
+
2611
+ ## [v2023.08.24](https://github.com/kivy/kivy-ios/tree/v2023.08.24)
2612
+
2613
+ [Full Changelog](https://github.com/kivy/kivy-ios/compare/v2023.05.21...v2023.08.24)
2614
+
2615
+ **Fixed bugs:**
2616
+
2617
+ - ld: 407 duplicate symbols for architecture arm64 [\#787](https://github.com/kivy/kivy-ios/issues/787)
2618
+
2619
+ **Closed issues:**
2620
+
2621
+ - please delete [\#846](https://github.com/kivy/kivy-ios/issues/846)
2622
+ - Label and MDLabel don't work correctly on iOS [\#845](https://github.com/kivy/kivy-ios/issues/845)
2623
+ - ImportError: dynamic module does not define module export function \(PyInit\_\_bcrypt\) [\#841](https://github.com/kivy/kivy-ios/issues/841)
2624
+ - Build failure [\#838](https://github.com/kivy/kivy-ios/issues/838)
2625
+ - Custom recipes just like p4a [\#837](https://github.com/kivy/kivy-ios/issues/837)
2626
+ - pandas recipe [\#836](https://github.com/kivy/kivy-ios/issues/836)
2627
+ - Build numpy: '\['/kivy/dist/hostpython3/bin/python', '-m', 'cython', '-3', '--fast-fail', '-o', '\_philox.c', '\_philox.pyx'\]' returned non-zero exit status 1.' [\#835](https://github.com/kivy/kivy-ios/issues/835)
2628
+ - ios.c not found when building ios for kivy [\#833](https://github.com/kivy/kivy-ios/issues/833)
2629
+ - Imvaild symlink [\#825](https://github.com/kivy/kivy-ios/issues/825)
2630
+ - dynamic module does not define module export function \(PyInit\_\_multiarray\_umath\) [\#822](https://github.com/kivy/kivy-ios/issues/822)
2631
+ - Toolchain build openssl not working [\#819](https://github.com/kivy/kivy-ios/issues/819)
2632
+ - OSError: codec configuration error when reading image file [\#818](https://github.com/kivy/kivy-ios/issues/818)
2633
+ - Command PhaseScriptExecution failed with a nonzero exit code [\#817](https://github.com/kivy/kivy-ios/issues/817)
2634
+ - We discovered that the content didn’t load after launched. [\#816](https://github.com/kivy/kivy-ios/issues/816)
2635
+ - Asset validation failed Invalid bundle structure. The “soccer.app/lib/python3.10/site-packages/charset\_normalizer/md.cpython-310-darwin.so” binary file is not permitted. [\#815](https://github.com/kivy/kivy-ios/issues/815)
2636
+ - Asset validation failed Invalid Bundle Structure for libnpyrandom.a and libnpymath.a [\#814](https://github.com/kivy/kivy-ios/issues/814)
2637
+ - kivy-ios Not found '\_SHA256.so' [\#813](https://github.com/kivy/kivy-ios/issues/813)
2638
+ - The toggling of the 'readonly' attribute \(True/False\) for the MDTextField is not functioning as expected in the xcode build [\#811](https://github.com/kivy/kivy-ios/issues/811)
2639
+ - libpng recipe won't build [\#808](https://github.com/kivy/kivy-ios/issues/808)
2640
+ - build error in Python3.8 [\#796](https://github.com/kivy/kivy-ios/issues/796)
2641
+
2642
+ **Merged pull requests:**
2643
+
2644
+ - Use a pinned version of `Cython` for now, as most of the recipes are incompatible with `Cython==3.x.x` [\#844](https://github.com/kivy/kivy-ios/pull/844) ([misl6](https://github.com/misl6))
2645
+ - Remove Unused Dependency: `requests` [\#840](https://github.com/kivy/kivy-ios/pull/840) ([gdrosos](https://github.com/gdrosos))
2646
+ - Fix download from `sourceforge` \(and possibly many others\) [\#830](https://github.com/kivy/kivy-ios/pull/830) ([misl6](https://github.com/misl6))
2647
+ - Now Github Actions provides `python3` via `setup-python` also for Apple Silicon macs [\#829](https://github.com/kivy/kivy-ios/pull/829) ([misl6](https://github.com/misl6))
2648
+ - Bump Kivy version to 2.2.1 [\#828](https://github.com/kivy/kivy-ios/pull/828) ([misl6](https://github.com/misl6))
2649
+ - Update `libpng` recipe and build `SDL2_ttf` vendored `freetype` with png support, so can render colored emoji [\#827](https://github.com/kivy/kivy-ios/pull/827) ([misl6](https://github.com/misl6))
2650
+ - SDL\_ttf: Hide internal symbols via single-object prelink [\#820](https://github.com/kivy/kivy-ios/pull/820) ([misl6](https://github.com/misl6))
2651
+ - Update zbarlight recipe to 3.0, refactor [\#805](https://github.com/kivy/kivy-ios/pull/805) ([Cheaterman](https://github.com/Cheaterman))
2652
+ - Refactor biglink ; add extra assertions to help debug failures [\#804](https://github.com/kivy/kivy-ios/pull/804) ([Cheaterman](https://github.com/Cheaterman))
2653
+
2654
+
2655
+ ## [v2023.05.21](https://github.com/kivy/kivy-ios/tree/v2023.05.21)
2656
+
2657
+ [Full Changelog](https://github.com/kivy/kivy-ios/compare/v2023.01.29...v2023.05.21)
2658
+
2659
+ **Closed issues:**
2660
+
2661
+ - Issue adding Pillow to Kivy-ios build [\#807](https://github.com/kivy/kivy-ios/issues/807)
2662
+ - I have created and app using kivy. it works in python through VS code but when I run through Xcode it gets no module named "application name" in main.py, line number 4 error and close the app in simulator. I tried all the ways in previous discussions but still not working. is there any other way I can try? [\#800](https://github.com/kivy/kivy-ios/issues/800)
2663
+ - Kivy ios save an screen as an image to the users ios photos [\#799](https://github.com/kivy/kivy-ios/issues/799)
2664
+ - Command PhaseScriptExecution failed with a nonzero exit code [\#793](https://github.com/kivy/kivy-ios/issues/793)
2665
+ - ImportError: PyInit\_interval [\#788](https://github.com/kivy/kivy-ios/issues/788)
2666
+ - Recipe for the cryptography module [\#786](https://github.com/kivy/kivy-ios/issues/786)
2667
+ - Pyaudio [\#785](https://github.com/kivy/kivy-ios/issues/785)
2668
+ - Check \#783 [\#784](https://github.com/kivy/kivy-ios/issues/784)
2669
+ - Command PhaseScriptExecution failed with a nonzero exit code [\#783](https://github.com/kivy/kivy-ios/issues/783)
2670
+ - Cocoapods import not found when kivy is installed [\#777](https://github.com/kivy/kivy-ios/issues/777)
2671
+ - sh.CommandNotFound: /Users/suhaan/Documents/weatherappkivy/dist/hostpython3/bin/pip3 [\#775](https://github.com/kivy/kivy-ios/issues/775)
2672
+ - check \#773 issue [\#774](https://github.com/kivy/kivy-ios/issues/774)
2673
+ - Asset validation failed Invalid bundle structure. The “theweatherreporterapp.app/lib/python3.9/site-packages/charset\_normalizer/md\_\_mypyc.cpython-39-darwin.so” binary file is not permitted. Your app cannot contain standalone executables or libraries, other than a valid CFBundleExecutable of supported bundles. For details, visit: https://developer.apple.com/documentation/bundleresources/placing\_content\_in\_a\_bundle \(ID: eb2a7a62-515c-459b-a222-5330e609551d\) [\#773](https://github.com/kivy/kivy-ios/issues/773)
2674
+ - dynamic module does not define module export function \(PyInit\_md\) error [\#772](https://github.com/kivy/kivy-ios/issues/772)
2675
+ - Toolchain 407 duplicate error of arm64 [\#771](https://github.com/kivy/kivy-ios/issues/771)
2676
+ - bash: toolchain: command not found [\#770](https://github.com/kivy/kivy-ios/issues/770)
2677
+ - when running kivyforge build python3 kivy pillow with arch arm64 with apple sicion getting list index out of range [\#769](https://github.com/kivy/kivy-ios/issues/769)
2678
+ - Error compiling SDL\_Image [\#763](https://github.com/kivy/kivy-ios/issues/763)
2679
+ - ImportError: dynamic module does not define module export function \(PyInit\_PIL\_\_imaging\) [\#711](https://github.com/kivy/kivy-ios/issues/711)
2680
+ - Build python3 with mmap module [\#659](https://github.com/kivy/kivy-ios/issues/659)
2681
+
2682
+ **Merged pull requests:**
2683
+
2684
+ - Fix AnyIO \(and asks\) support [\#803](https://github.com/kivy/kivy-ios/pull/803) ([Cheaterman](https://github.com/Cheaterman))
2685
+ - Update Kivy recipe for 2.2.0 [\#802](https://github.com/kivy/kivy-ios/pull/802) ([misl6](https://github.com/misl6))
2686
+ - Use `--no-deps` and `--platform` during install of recipe python deps [\#801](https://github.com/kivy/kivy-ios/pull/801) ([misl6](https://github.com/misl6))
2687
+ - Fixes crash on ios.get\_safe\_area\(\) in iOS 16 [\#790](https://github.com/kivy/kivy-ios/pull/790) ([tito](https://github.com/tito))
2688
+ - Update bridge.m/h to make plyer works with severals implementation [\#789](https://github.com/kivy/kivy-ios/pull/789) ([tito](https://github.com/tito))
2689
+ - Bump `numpy` version to `1.24.2` \( and fix the build on Python 3.10 ✅ \) [\#780](https://github.com/kivy/kivy-ios/pull/780) ([misl6](https://github.com/misl6))
2690
+ - Disable SDL2 hidapi in order to avoid dependency on `CoreBluetooth` [\#779](https://github.com/kivy/kivy-ios/pull/779) ([misl6](https://github.com/misl6))
2691
+ - 🎉 Python 3.10 support [\#768](https://github.com/kivy/kivy-ios/pull/768) ([misl6](https://github.com/misl6))
2692
+ - Upgrade more GitHub Actions plus Python 3.11.1 and Cython 0.29.33 [\#767](https://github.com/kivy/kivy-ios/pull/767) ([cclauss](https://github.com/cclauss))
2693
+ - Upgrade GitHub Actions [\#765](https://github.com/kivy/kivy-ios/pull/765) ([cclauss](https://github.com/cclauss))
2694
+ - Update required Xcode version to 13 [\#764](https://github.com/kivy/kivy-ios/pull/764) ([misl6](https://github.com/misl6))
2695
+ - Update ffmpeg/ffpyplayer versions [\#716](https://github.com/kivy/kivy-ios/pull/716) ([tito](https://github.com/tito))
2696
+
2697
+
2698
+ ## [v2023.01.29](https://github.com/kivy/kivy-ios/tree/v2023.01.29)
2699
+
2700
+ [Full Changelog](https://github.com/kivy/kivy-ios/compare/v2022.07.19...v2023.01.29)
2701
+
2702
+ **Closed issues:**
2703
+
2704
+ - Unknown class `<BoxShadow>` [\#758](https://github.com/kivy/kivy-ios/issues/758)
2705
+ - toolchain pip3 install pycryptodome error [\#755](https://github.com/kivy/kivy-ios/issues/755)
2706
+ - Stuck between two error codes: ImportError...\(PyInit\_\_imaging\) and 407 duplicate symbols [\#752](https://github.com/kivy/kivy-ios/issues/752)
2707
+ - Toolchain Build error - commandnotfound python [\#748](https://github.com/kivy/kivy-ios/issues/748)
2708
+ - No module named 'xxx' [\#744](https://github.com/kivy/kivy-ios/issues/744)
2709
+ - dynamic module does not define module export function \(PyInit\_\_sqlite3\) [\#743](https://github.com/kivy/kivy-ios/issues/743)
2710
+ - \[BUG\] Kivy Ios on mac os monterey arm64 simulator [\#740](https://github.com/kivy/kivy-ios/issues/740)
2711
+ - PhaseScriptExecution failed when hitting Play in Xcode [\#739](https://github.com/kivy/kivy-ios/issues/739)
2712
+ - toolchain.py build python3 openssl kivy not completing after upgrading macOS Monterey 12.5 [\#738](https://github.com/kivy/kivy-ios/issues/738)
2713
+ - Proposal of versioning numbering method change for the next release [\#729](https://github.com/kivy/kivy-ios/issues/729)
2714
+ - xcode archive failure- missing signing identifier ... site-packages/google/...\_api\_implementation.cpython-39-darwin.so [\#697](https://github.com/kivy/kivy-ios/issues/697)
2715
+
2716
+ **Merged pull requests:**
2717
+
2718
+ - Update pbxproj pinned version in requirements.txt [\#757](https://github.com/kivy/kivy-ios/pull/757) ([misl6](https://github.com/misl6))
2719
+ - Add default Launch Screen storyboard [\#756](https://github.com/kivy/kivy-ios/pull/756) ([misl6](https://github.com/misl6))
2720
+ - Flake8 does not support inline comments for any of the keys [\#754](https://github.com/kivy/kivy-ios/pull/754) ([misl6](https://github.com/misl6))
2721
+ - Use Python 3.10 for tests [\#750](https://github.com/kivy/kivy-ios/pull/750) ([misl6](https://github.com/misl6))
2722
+ - Update SDL2, SDL2\_ttf, SDL2\_mixer, SDL2\_image to latest releases [\#749](https://github.com/kivy/kivy-ios/pull/749) ([misl6](https://github.com/misl6))
2723
+ - :rotating\_light: Minor linting fixes/updates [\#747](https://github.com/kivy/kivy-ios/pull/747) ([AndreMiras](https://github.com/AndreMiras))
2724
+ - Fixes some E275 - assert is a keyword [\#746](https://github.com/kivy/kivy-ios/pull/746) ([misl6](https://github.com/misl6))
2725
+ - Use `sys.executable` instead of `sh.python` [\#745](https://github.com/kivy/kivy-ios/pull/745) ([misl6](https://github.com/misl6))
2726
+ - Multiple fixes to README [\#737](https://github.com/kivy/kivy-ios/pull/737) ([rshah713](https://github.com/rshah713))
2727
+ - Fix typo in README [\#736](https://github.com/kivy/kivy-ios/pull/736) ([rshah713](https://github.com/rshah713))
2728
+ - Disable configuration of i386-Simulator for libffi [\#761](https://github.com/kivy/kivy-ios/pull/761) ([misl6](https://github.com/misl6)
2729
+
2730
+ ## [2022.07.19] - 2022-07-19
2731
+
2732
+ ## Added
2733
+ - Added py3dns recipe [\#728](https://github.com/kivy/kivy-ios/pull/728) ([Neizvestnyj](https://github.com/Neizvestnyj))
2734
+ - Ignore egg-info in git [\#718](https://github.com/kivy/kivy-ios/pull/718) ([tito](https://github.com/tito))
2735
+ - Bump Kivy recipe to a commit that includes latest camera API enhancements [\#700](https://github.com/kivy/kivy-ios/pull/700) ([RobertFlatt](https://github.com/RobertFlatt))
2736
+ - Add support-requests v2 [\#689](https://github.com/kivy/kivy-ios/pull/689) ([misl6](https://github.com/misl6))
2737
+
2738
+ ## Fixed
2739
+ - Updated shebangs to target python3. Bumped libffi version to 3.4.2 [\#688](https://github.com/kivy/kivy-ios/pull/688) ([misl6](https://github.com/misl6))
2740
+ - Fixes libffi 3.4.2 not linking correctly (missing symbols) on iOS Simulator [\#695](https://github.com/kivy/kivy-ios/pull/695) ([misl6](https://github.com/misl6))
2741
+ - Update dpi for latest iPhone models [\#707](https://github.com/kivy/kivy-ios/pull/707) ([akshayaurora](https://github.com/akshayaurora))
2742
+ - Our self-hosted Apple Silicon runner now has been migrated to actions/runner v2.292.0 which now supports arm64 natively [\#710](https://github.com/kivy/kivy-ios/pull/710) ([misl6](https://github.com/misl6))
2743
+ - Update xcassets to include mandatory icons in latest xcode [\#717](https://github.com/kivy/kivy-ios/pull/717) ([tito](https://github.com/tito))
2744
+ - Bump cookiecutter from 1.7.2 to 2.1.1 [\#714](https://github.com/kivy/kivy-ios/pull/714) ([dependabot](https://github.com/dependabot))
2745
+ - Use shutil.which instead of sh.which [\#735](https://github.com/kivy/kivy-ios/pull/735) ([misl6](https://github.com/misl6))
2746
+
2747
+ ## [2022.07.19] - 2022-07-19
2748
+
2749
+ ## Added
2750
+ - Added py3dns recipe [\#728](https://github.com/kivy/kivy-ios/pull/728) ([Neizvestnyj](https://github.com/Neizvestnyj))
2751
+ - Ignore egg-info in git [\#718](https://github.com/kivy/kivy-ios/pull/718) ([tito](https://github.com/tito))
2752
+ - Bump Kivy recipe to a commit that includes latest camera API enhancements [\#700](https://github.com/kivy/kivy-ios/pull/700) ([RobertFlatt](https://github.com/RobertFlatt))
2753
+ - Add support-requests v2 [\#689](https://github.com/kivy/kivy-ios/pull/689) ([misl6](https://github.com/misl6))
2754
+
2755
+ ## Fixed
2756
+ - Updated shebangs to target python3. Bumped libffi version to 3.4.2 [\#688](https://github.com/kivy/kivy-ios/pull/688) ([misl6](https://github.com/misl6))
2757
+ - Fixes libffi 3.4.2 not linking correctly (missing symbols) on iOS Simulator [\#695](https://github.com/kivy/kivy-ios/pull/695) ([misl6](https://github.com/misl6))
2758
+ - Update dpi for latest iPhone models [\#707](https://github.com/kivy/kivy-ios/pull/707) ([akshayaurora](https://github.com/akshayaurora))
2759
+ - Our self-hosted Apple Silicon runner now has been migrated to actions/runner v2.292.0 which now supports arm64 natively [\#710](https://github.com/kivy/kivy-ios/pull/710) ([misl6](https://github.com/misl6))
2760
+ - Update xcassets to include mandatory icons in latest xcode [\#717](https://github.com/kivy/kivy-ios/pull/717) ([tito](https://github.com/tito))
2761
+ - Bump cookiecutter from 1.7.2 to 2.1.1 [\#714](https://github.com/kivy/kivy-ios/pull/714) ([dependabot](https://github.com/dependabot))
2762
+ - Use shutil.which instead of sh.which [\#735](https://github.com/kivy/kivy-ios/pull/735) ([misl6](https://github.com/misl6))
2763
+
2764
+ ## [1.3.0] - 2022-03-13
2765
+
2766
+ ### Added
2767
+ - Add native support for Apple Silicon [\#660](https://github.com/kivy/kivy-ios/pull/660) ([misl6](https://github.com/misl6))
2768
+ - Makes \_bz2 module available [\#658](https://github.com/kivy/kivy-ios/pull/658) ([misl6](https://github.com/misl6))
2769
+ - enable sdl2 image provider to allow image to be saved from widget [\#656](https://github.com/kivy/kivy-ios/pull/656) ([brentpicasso](https://github.com/brentpicasso))
2770
+ - Add MANIFEST.in [\#651](https://github.com/kivy/kivy-ios/pull/651) ([misl6](https://github.com/misl6))
2771
+ - Add official OSI name in the license metadata [\#639](https://github.com/kivy/kivy-ios/pull/639) ([ecederstrand](https://github.com/ecederstrand))
2772
+ - Enables buildozer to automatically install dependencies [\#638](https://github.com/kivy/kivy-ios/pull/638) ([meow464](https://github.com/meow464))
2773
+ - Add bitcode support [\#605](https://github.com/kivy/kivy-ios/pull/605) ([kewlbear](https://github.com/kewlbear))
2774
+ - Add the ability to clean a custom recipe, use the custom recipe instead of the bundled one, if available. [\#602](https://github.com/kivy/kivy-ios/pull/602) ([misl6](https://github.com/misl6))
2775
+ - Add an error to FAQ [\#586](https://github.com/kivy/kivy-ios/pull/586) ([mvasilkov](https://github.com/mvasilkov))
2776
+
2777
+ ### Removed
2778
+ - Remove NO\_CONFIG and NO\_FILE\_LOG [\#670](https://github.com/kivy/kivy-ios/pull/670) ([akshayaurora](https://github.com/akshayaurora))
2779
+ - Remove i386 and armv7 references from docs and code. Both are unsupported since a while. [\#610](https://github.com/kivy/kivy-ios/pull/610) ([misl6](https://github.com/misl6))
2780
+
2781
+ ### Fixed
2782
+ - Bump Kivy version to 2.1.0 [\#679](https://github.com/kivy/kivy-ios/pull/679) ([misl6](https://github.com/misl6))
2783
+ - CI: Use a Kivy-ios installation for tests, instead of the legacy toolchain.py [\#653](https://github.com/kivy/kivy-ios/pull/653) ([misl6](https://github.com/misl6))
2784
+ - Fix outdated freetype url [\#632](https://github.com/kivy/kivy-ios/pull/632) ([misl6](https://github.com/misl6))
2785
+ - Bump sdl2\_ttf to 2.0.15 to improve compatibility with kivymd [\#624](https://github.com/kivy/kivy-ios/pull/624) ([brentpicasso](https://github.com/brentpicasso))
2786
+ - add leading whitespace for include file [\#623](https://github.com/kivy/kivy-ios/pull/623) ([brentpicasso](https://github.com/brentpicasso))
2787
+ - fix icon generation [\#620](https://github.com/kivy/kivy-ios/pull/620) ([akshayaurora](https://github.com/akshayaurora))
2788
+ - Switch to iOS 9.0 where applicable [\#611](https://github.com/kivy/kivy-ios/pull/611) ([misl6](https://github.com/misl6))
2789
+ - Pillow recipe rework + upgrade to version 8.2.0 [\#606](https://github.com/kivy/kivy-ios/pull/606) ([misl6](https://github.com/misl6))
2790
+ - Update numpy to version 1.20.2 [\#604](https://github.com/kivy/kivy-ios/pull/604) ([misl6](https://github.com/misl6))
2791
+ - Upgrade python version to 3.9.2 [\#601](https://github.com/kivy/kivy-ios/pull/601) ([misl6](https://github.com/misl6))
2792
+
2793
+ ## [1.2.1] - 2021-01-26
2794
+ ### Fixed
2795
+ - Fixes hostpython3 recipe on MacOS 11.1 BigSur [\#581](https://github.com/kivy/kivy-ios/issues/581) ([misl6](https://github.com/misl6))
2796
+ - Fixes iOS Simulator on latest Xcode [\#571](https://github.com/kivy/kivy-ios/issues/571) ([misl6](https://github.com/misl6))
2797
+ - Fixes Pillow build on Xcode 12.2 [\#579](https://github.com/kivy/kivy-ios/issues/579) ([misl6](https://github.com/misl6))
2798
+ - Cookiecutter: Fixes header and library search paths on Release [\#582](https://github.com/kivy/kivy-ios/issues/582) ([misl6](https://github.com/misl6))
2799
+
2800
+ ## [1.2.0] - 2020-12-26
2801
+ ### Added
2802
+ - :books: Advise on using a venv [\#495](https://github.com/kivy/kivy-ios/issues/495) ([AndreMiras](https://github.com/AndreMiras))
2803
+ - Add custom recipes [\#417](https://github.com/kivy/kivy-ios/issues/417) ([misl6](https://github.com/misl6))
2804
+ - Add feature request template [\#557](https://github.com/kivy/kivy-ios/issues/557) ([Zen-CODE](https://github.com/Zen-CODE))
2805
+ - Add instruction for cleaning cache and the build directory [\#558](https://github.com/kivy/kivy-ios/issues/558) ([Zen-CODE](https://github.com/Zen-CODE))
2806
+
2807
+ ### Removed
2808
+ - Remove distribute recipe [\#507](https://github.com/kivy/kivy-ios/issues/507) ([Zen-CODE](https://github.com/Zen-CODE))
2809
+ - Remove PY2 legacy blocks from coockiecutter main.m [\#522](https://github.com/kivy/kivy-ios/issues/522) ([Zen-CODE](https://github.com/Zen-CODE))
2810
+ - Remove outdated pil and pkgresources recipes [\#524](https://github.com/kivy/kivy-ios/issues/524) ([Zen-CODE](https://github.com/Zen-CODE))
2811
+ - Removed broken packages listing for non-existing packages [\#525](https://github.com/kivy/kivy-ios/issues/525) ([Zen-CODE](https://github.com/Zen-CODE))
2812
+
2813
+ ### Fixed
2814
+ - :bug: fixes flake8 errors post update [\#496](https://github.com/kivy/kivy-ios/issues/496) ([AndreMiras](https://github.com/AndreMiras))
2815
+ - Updates netifaces recipe, leverages `python_depends` [\#490](https://github.com/kivy/kivy-ios/issues/490) ([AndreMiras](https://github.com/AndreMiras))
2816
+ - Fix pillow recipe [\#498](https://github.com/kivy/kivy-ios/issues/498) ([Zen-CODE](https://github.com/Zen-CODE))
2817
+ - Fix/pillow [\#500](https://github.com/kivy/kivy-ios/issues/500) ([Zen-CODE](https://github.com/Zen-CODE))
2818
+ - Fix wekzeug recipe [\#501](https://github.com/kivy/kivy-ios/issues/501) ([Zen-CODE](https://github.com/Zen-CODE))
2819
+ - Fix markupsafe recipe [\#505](https://github.com/kivy/kivy-ios/issues/505) ([Zen-CODE](https://github.com/Zen-CODE))
2820
+ - Fix jinja2 recipe, remove from broken list [\#508](https://github.com/kivy/kivy-ios/issues/508) ([Zen-CODE](https://github.com/Zen-CODE))
2821
+ - Fix werkzeug recipe [\#509](https://github.com/kivy/kivy-ios/issues/509) ([Zen-CODE](https://github.com/Zen-CODE))
2822
+ - Pin flask version and dependencies [\#510](https://github.com/kivy/kivy-ios/issues/510) ([Zen-CODE](https://github.com/Zen-CODE))
2823
+ - :bug: Updates libffi download link [\#516](https://github.com/kivy/kivy-ios/issues/516) ([AndreMiras](https://github.com/AndreMiras))
2824
+ - Remove --ignore-installed [\#521](https://github.com/kivy/kivy-ios/issues/521) ([misl6](https://github.com/misl6))
2825
+ - Remove compile cruft after build [\#523](https://github.com/kivy/kivy-ios/issues/523) ([Zen-CODE](https://github.com/Zen-CODE))
2826
+ - Move Cython requirement into requirements.txt [\#531](https://github.com/kivy/kivy-ios/issues/531) ([Zen-CODE](https://github.com/Zen-CODE))
2827
+ - Fix/host setuptools3 [\#533](https://github.com/kivy/kivy-ios/issues/533) ([Zen-CODE](https://github.com/Zen-CODE))
2828
+ - Fixes CI [\#569](https://github.com/kivy/kivy-ios/issues/569) ([misl6](https://github.com/misl6))
2829
+ - Fix build on Xcode 12.2 [\#568](https://github.com/kivy/kivy-ios/issues/568) ([misl6](https://github.com/misl6))
2830
+ - Update openssl and ffmpeg version to fix build issues [\#562](https://github.com/kivy/kivy-ios/issues/562) ([Zen-CODE](https://github.com/Zen-CODE))
2831
+ - Update kivy to a post 2.0.0 version [\#575](https://github.com/kivy/kivy-ios/issues/575) ([misl6](https://github.com/misl6))
2832
+
2833
+ ## [1.1.2] - 2020-05-11
2834
+ ### Fixed
2835
+ - Fixes (venv build) reference to SDL\_main.h [\#493](https://github.com/kivy/kivy-ios/issues/493) ([AndreMiras](https://github.com/AndreMiras))
2836
+
2837
+ ## [1.1.1] - 2020-05-11
2838
+ ### Added
2839
+ - Add python depends [\#455](https://github.com/kivy/kivy-ios/issues/455) ([misl6](https://github.com/misl6))
2840
+
2841
+ ### Removed
2842
+ - Removed Python 2 support [\#482](https://github.com/kivy/kivy-ios/issues/482) ([AndreMiras](https://github.com/AndreMiras))
2843
+
2844
+ ### Fixed
2845
+ - Adds initial\_working\_directory [\#489](https://github.com/kivy/kivy-ios/issues/489) ([misl6](https://github.com/misl6))
2846
+ - Adds netifaces recipe, closes #239 [\#488](https://github.com/kivy/kivy-ios/issues/488) ([AndreMiras](https://github.com/AndreMiras))
2847
+ - Uses contextlib.suppress to ignore exceptions [\#487](https://github.com/kivy/kivy-ios/issues/487) ([AndreMiras](https://github.com/AndreMiras))
2848
+ - DRY via the find\_xcodeproj() helper method [\#486](https://github.com/kivy/kivy-ios/issues/486) ([AndreMiras](https://github.com/AndreMiras))
2849
+ - Uses cd context manager in Python3Recipe.reduce\_python() [\#485](https://github.com/kivy/kivy-ios/issues/485) ([AndreMiras](https://github.com/AndreMiras))
2850
+ - Uses Python 3 syntax [\#484](https://github.com/kivy/kivy-ios/issues/484) ([AndreMiras](https://github.com/AndreMiras))
2851
+ - Also lints the tools/ folder [\#483](https://github.com/kivy/kivy-ios/issues/483) ([AndreMiras](https://github.com/AndreMiras))
2852
+ - Migrates libffi build to Python 3 [\#481](https://github.com/kivy/kivy-ios/issues/481) ([AndreMiras](https://github.com/AndreMiras))
2853
+ - Uses a couple of syntax shortcuts [\#479](https://github.com/kivy/kivy-ios/issues/479) ([AndreMiras](https://github.com/AndreMiras))
2854
+ - Takes ToolchainCL definition outside the main [\#478](https://github.com/kivy/kivy-ios/issues/478) ([AndreMiras](https://github.com/AndreMiras))
2855
+
2856
+ ## [1.1.0] - 2020-05-05
2857
+ ### Added
2858
+ - Automatically publish to PyPI upon tagging [\#475](https://github.com/kivy/kivy-ios/issues/475) ([AndreMiras](https://github.com/AndreMiras))
2859
+ - Dedicated setup.py test workflow [\#474](https://github.com/kivy/kivy-ios/issues/474) ([AndreMiras](https://github.com/AndreMiras))
2860
+
2861
+ ### Fixed
2862
+ - Fixes a regression introduced during the linting [\#477](https://github.com/kivy/kivy-ios/issues/477) ([AndreMiras](https://github.com/AndreMiras))
2863
+ - More fixes to Numpy so that the binary is accepted by the App Store [\#473](https://github.com/kivy/kivy-ios/issues/473) ([lerela](https://github.com/lerela))
2864
+ - Do not build known broken recipes [\#471](https://github.com/kivy/kivy-ios/issues/471) ([AndreMiras](https://github.com/AndreMiras))
2865
+ - Fixes minor typos in the issue template [\#469](https://github.com/kivy/kivy-ios/issues/469) ([AndreMiras](https://github.com/AndreMiras))
2866
+ - Activates venv before venv build [\#464](https://github.com/kivy/kivy-ios/issues/464) ([AndreMiras](https://github.com/AndreMiras))
2867
+ - Fixes building in venv [\#462](https://github.com/kivy/kivy-ios/issues/462) ([AndreMiras](https://github.com/AndreMiras))
2868
+ - Cleanup - Removes vendored deps [\#454](https://github.com/kivy/kivy-ios/issues/454) ([misl6](https://github.com/misl6))
2869
+
2870
+ ### Changed
2871
+ - Updates README.md with install/usage from PyPI [\#476](https://github.com/kivy/kivy-ios/issues/476) ([AndreMiras](https://github.com/AndreMiras))
2872
+ - Moving to dedicated kivy\_ios/ package directory [\#472](https://github.com/kivy/kivy-ios/issues/472) ([AndreMiras](https://github.com/AndreMiras))
2873
+ - Bumps Cython version [\#470](https://github.com/kivy/kivy-ios/issues/470) ([misl6](https://github.com/misl6))
2874
+ - Uses new `cd` context manager more [\#465](https://github.com/kivy/kivy-ios/issues/465) ([AndreMiras](https://github.com/AndreMiras))
2875
+
2876
+
2877
+ ## [1.0.0] - 2020-05-02
2878
+ - Initial release