kivyforge 3.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. kivyforge/__init__.py +9 -0
  2. kivyforge/artifacts/__init__.py +41 -0
  3. kivyforge/artifacts/cache.py +81 -0
  4. kivyforge/artifacts/collect.py +354 -0
  5. kivyforge/artifacts/download.py +125 -0
  6. kivyforge/artifacts/frameworks.py +240 -0
  7. kivyforge/artifacts/native_stage_util.py +346 -0
  8. kivyforge/artifacts/runtime.py +65 -0
  9. kivyforge/artifacts/verify.py +45 -0
  10. kivyforge/artifacts/wheels.py +89 -0
  11. kivyforge/bundle/__init__.py +1 -0
  12. kivyforge/bundle/pycompile.py +161 -0
  13. kivyforge/cli/__init__.py +52 -0
  14. kivyforge/cli/_common.py +62 -0
  15. kivyforge/cli/_platform.py +84 -0
  16. kivyforge/cli/build.py +111 -0
  17. kivyforge/cli/clean.py +135 -0
  18. kivyforge/cli/doctor.py +52 -0
  19. kivyforge/cli/init.py +612 -0
  20. kivyforge/cli/init_writer.py +716 -0
  21. kivyforge/cli/lock.py +261 -0
  22. kivyforge/cli/open_cmd.py +15 -0
  23. kivyforge/cli/package.py +134 -0
  24. kivyforge/cli/run.py +162 -0
  25. kivyforge/cli/status.py +20 -0
  26. kivyforge/cli/upgrade.py +314 -0
  27. kivyforge/config/__init__.py +46 -0
  28. kivyforge/config/errors.py +43 -0
  29. kivyforge/config/icons.py +60 -0
  30. kivyforge/config/loader.py +3079 -0
  31. kivyforge/config/model.py +817 -0
  32. kivyforge/doctor/__init__.py +19 -0
  33. kivyforge/doctor/checks_common.py +95 -0
  34. kivyforge/doctor/probe.py +357 -0
  35. kivyforge/doctor/result.py +41 -0
  36. kivyforge/lock/__init__.py +42 -0
  37. kivyforge/lock/_pip_shim.py +63 -0
  38. kivyforge/lock/find_links.py +182 -0
  39. kivyforge/lock/model.py +69 -0
  40. kivyforge/lock/pep751.py +111 -0
  41. kivyforge/lock/reader.py +30 -0
  42. kivyforge/lock/resolver.py +124 -0
  43. kivyforge/lock/wheelruntime/__init__.py +70 -0
  44. kivyforge/lock/wheelruntime/builder.py +280 -0
  45. kivyforge/lock/wheelruntime/model.py +94 -0
  46. kivyforge/lock/wheelruntime/native_binaries.py +127 -0
  47. kivyforge/lock/wheelruntime/pbs_github.py +133 -0
  48. kivyforge/lock/wheelruntime/profile.py +104 -0
  49. kivyforge/lock/wheelruntime/resolver.py +248 -0
  50. kivyforge/lock/wheelruntime/runtime.py +161 -0
  51. kivyforge/lock/wheelruntime/serialize.py +245 -0
  52. kivyforge/platforms/__init__.py +114 -0
  53. kivyforge/platforms/android/__init__.py +140 -0
  54. kivyforge/platforms/android/adb.py +215 -0
  55. kivyforge/platforms/android/bootstrap/__init__.py +7 -0
  56. kivyforge/platforms/android/bootstrap/contract.py +166 -0
  57. kivyforge/platforms/android/bootstrap/render.py +244 -0
  58. kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
  59. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
  60. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
  61. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
  62. kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
  63. kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
  64. kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
  65. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
  66. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
  67. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
  68. kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
  69. kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
  70. kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
  71. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
  72. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
  73. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
  74. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
  75. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
  76. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
  77. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
  78. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
  79. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
  80. kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
  81. kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
  82. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
  83. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
  84. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
  85. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
  86. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
  87. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
  88. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
  89. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
  90. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
  91. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
  92. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
  93. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
  94. kivyforge/platforms/android/cli.py +1023 -0
  95. kivyforge/platforms/android/doctor.py +899 -0
  96. kivyforge/platforms/android/elf.py +148 -0
  97. kivyforge/platforms/android/generate/__init__.py +1 -0
  98. kivyforge/platforms/android/generate/manifest.py +261 -0
  99. kivyforge/platforms/android/generate/project.py +462 -0
  100. kivyforge/platforms/android/generate/services.py +187 -0
  101. kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
  102. kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
  103. kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
  104. kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
  105. kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
  106. kivyforge/platforms/android/gradlew.py +60 -0
  107. kivyforge/platforms/android/icons.py +237 -0
  108. kivyforge/platforms/android/lock/__init__.py +1 -0
  109. kivyforge/platforms/android/lock/archives.py +109 -0
  110. kivyforge/platforms/android/lock/builder.py +387 -0
  111. kivyforge/platforms/android/lock/markers.py +71 -0
  112. kivyforge/platforms/android/lock/maven.py +252 -0
  113. kivyforge/platforms/android/lock/model.py +113 -0
  114. kivyforge/platforms/android/lock/python_meta.py +199 -0
  115. kivyforge/platforms/android/lock/reader.py +206 -0
  116. kivyforge/platforms/android/lock/resolver.py +276 -0
  117. kivyforge/platforms/android/lock/writer.py +121 -0
  118. kivyforge/platforms/android/policy.py +214 -0
  119. kivyforge/platforms/android/signing.py +145 -0
  120. kivyforge/platforms/android/smoke.py +43 -0
  121. kivyforge/platforms/android/splash.py +293 -0
  122. kivyforge/platforms/android/stage/__init__.py +1 -0
  123. kivyforge/platforms/android/stage/bundle.py +295 -0
  124. kivyforge/platforms/android/stage/jnilibs.py +186 -0
  125. kivyforge/platforms/android/stage/runtime.py +61 -0
  126. kivyforge/platforms/android/stage/wheels.py +106 -0
  127. kivyforge/platforms/android/toolchain.py +29 -0
  128. kivyforge/platforms/base.py +157 -0
  129. kivyforge/platforms/ios/__init__.py +129 -0
  130. kivyforge/platforms/ios/assets.py +193 -0
  131. kivyforge/platforms/ios/buildsettings.py +128 -0
  132. kivyforge/platforms/ios/cli.py +543 -0
  133. kivyforge/platforms/ios/doctor.py +410 -0
  134. kivyforge/platforms/ios/entitlements.py +34 -0
  135. kivyforge/platforms/ios/generator.py +318 -0
  136. kivyforge/platforms/ios/lock/__init__.py +71 -0
  137. kivyforge/platforms/ios/lock/builder.py +393 -0
  138. kivyforge/platforms/ios/lock/markers.py +69 -0
  139. kivyforge/platforms/ios/lock/model.py +95 -0
  140. kivyforge/platforms/ios/lock/python_meta.py +187 -0
  141. kivyforge/platforms/ios/lock/reader.py +191 -0
  142. kivyforge/platforms/ios/lock/resolver.py +304 -0
  143. kivyforge/platforms/ios/lock/spm.py +217 -0
  144. kivyforge/platforms/ios/lock/writer.py +114 -0
  145. kivyforge/platforms/ios/lock/xcframework.py +188 -0
  146. kivyforge/platforms/ios/materialize.py +59 -0
  147. kivyforge/platforms/ios/plist.py +91 -0
  148. kivyforge/platforms/ios/privacy.py +39 -0
  149. kivyforge/platforms/ios/skeleton.py +165 -0
  150. kivyforge/platforms/ios/sources.py +63 -0
  151. kivyforge/platforms/ios/staging.py +225 -0
  152. kivyforge/platforms/ios/swift_packages.py +324 -0
  153. kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
  154. kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
  155. kivyforge/platforms/ios/templates/main.m +15 -0
  156. kivyforge/platforms/ios/templates/main_config.h.in +12 -0
  157. kivyforge/platforms/ios/xcframeworks.py +194 -0
  158. kivyforge/platforms/ios/xcode/__init__.py +76 -0
  159. kivyforge/platforms/ios/xcode/commands.py +296 -0
  160. kivyforge/platforms/ios/xcode/runner.py +316 -0
  161. kivyforge/platforms/linux/__init__.py +120 -0
  162. kivyforge/platforms/linux/appimage.py +169 -0
  163. kivyforge/platforms/linux/bundle.py +235 -0
  164. kivyforge/platforms/linux/cli.py +232 -0
  165. kivyforge/platforms/linux/desktop.py +71 -0
  166. kivyforge/platforms/linux/doctor.py +438 -0
  167. kivyforge/platforms/linux/elftools.py +95 -0
  168. kivyforge/platforms/linux/icons.py +129 -0
  169. kivyforge/platforms/linux/launcher.py +107 -0
  170. kivyforge/platforms/linux/lock/__init__.py +126 -0
  171. kivyforge/platforms/linux/lock/profile.py +254 -0
  172. kivyforge/platforms/linux/lock/runtime.py +75 -0
  173. kivyforge/platforms/linux/native_stage.py +47 -0
  174. kivyforge/platforms/linux/runtime_stage.py +107 -0
  175. kivyforge/platforms/linux/wheels_stage.py +103 -0
  176. kivyforge/platforms/macos/__init__.py +121 -0
  177. kivyforge/platforms/macos/bundle.py +255 -0
  178. kivyforge/platforms/macos/cli.py +269 -0
  179. kivyforge/platforms/macos/doctor.py +480 -0
  180. kivyforge/platforms/macos/icns.py +75 -0
  181. kivyforge/platforms/macos/launcher.py +118 -0
  182. kivyforge/platforms/macos/lock/__init__.py +112 -0
  183. kivyforge/platforms/macos/lock/profile.py +119 -0
  184. kivyforge/platforms/macos/lock/runtime.py +58 -0
  185. kivyforge/platforms/macos/machotools.py +138 -0
  186. kivyforge/platforms/macos/native_stage.py +47 -0
  187. kivyforge/platforms/macos/notarize.py +143 -0
  188. kivyforge/platforms/macos/plist.py +37 -0
  189. kivyforge/platforms/macos/runtime_stage.py +110 -0
  190. kivyforge/platforms/macos/signing.py +78 -0
  191. kivyforge/platforms/macos/wheels_stage.py +108 -0
  192. kivyforge/platforms/windows/__init__.py +133 -0
  193. kivyforge/platforms/windows/assets.py +76 -0
  194. kivyforge/platforms/windows/bundle.py +298 -0
  195. kivyforge/platforms/windows/cli.py +314 -0
  196. kivyforge/platforms/windows/doctor.py +563 -0
  197. kivyforge/platforms/windows/fsswap.py +87 -0
  198. kivyforge/platforms/windows/icons.py +64 -0
  199. kivyforge/platforms/windows/launcher/__init__.py +193 -0
  200. kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
  201. kivyforge/platforms/windows/launcher/launcher.c +379 -0
  202. kivyforge/platforms/windows/lock/__init__.py +112 -0
  203. kivyforge/platforms/windows/lock/profile.py +101 -0
  204. kivyforge/platforms/windows/lock/runtime.py +64 -0
  205. kivyforge/platforms/windows/naming.py +52 -0
  206. kivyforge/platforms/windows/native_stage.py +66 -0
  207. kivyforge/platforms/windows/petools.py +98 -0
  208. kivyforge/platforms/windows/rcedit.py +116 -0
  209. kivyforge/platforms/windows/runtime_stage.py +221 -0
  210. kivyforge/platforms/windows/signing.py +168 -0
  211. kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
  212. kivyforge/platforms/windows/vendor/NOTICE +17 -0
  213. kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
  214. kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
  215. kivyforge/platforms/windows/vendor/__init__.py +12 -0
  216. kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
  217. kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
  218. kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
  219. kivyforge/platforms/windows/wheels_stage.py +139 -0
  220. kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
  221. kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
  222. kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
  223. kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
  224. kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  225. kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,64 @@
1
+ """Generate the Windows app icon (windows-spec).
2
+
3
+ When the project supplies ``[tool.kivy.windows.icons].source`` (a 1024×1024 PNG,
4
+ validated like the other platforms) kivyforge renders it into a multi-resolution
5
+ Windows ``.ico`` (256/48/32/16 px) with [Pillow](https://python-pillow.org/) —
6
+ the optional ``kivyforge[windows]`` extra. The ``.ico`` is then patched into the
7
+ launcher's resources with ``rcedit`` at assembly time.
8
+
9
+ Unlike Linux (whose ``appimagetool`` requires an icon and so always gets a
10
+ generated default), Windows needs none: an app with no configured icon simply
11
+ keeps the executable's default shell icon, and Pillow is only needed when an
12
+ icon *is* configured.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from pathlib import Path
18
+
19
+ from kivyforge.config.icons import validate_icon_source
20
+ from kivyforge.config.model import Config
21
+
22
+ from . import WindowsBundleError
23
+
24
+ # Sizes embedded in the .ico, largest first (Windows picks per display context).
25
+ ICO_SIZES = (256, 48, 32, 16)
26
+
27
+
28
+ def stage_icon(config: Config, project_root: Path, dest: Path) -> Path | None:
29
+ """Render the app ``.ico`` at *dest*; return it, or ``None`` if unconfigured.
30
+
31
+ ``None`` means no ``[tool.kivy.windows.icons].source`` was set, and the
32
+ launcher keeps its default icon (no ``rcedit --set-icon``).
33
+ """
34
+ source = config.windows_required.icons.source
35
+ if not source:
36
+ return None
37
+
38
+ src = (project_root / source).resolve()
39
+ validate_icon_source(src)
40
+ image = _open(src)
41
+ dest.parent.mkdir(parents=True, exist_ok=True)
42
+ # Pillow writes every requested size into one .ico from the largest render.
43
+ image.save(dest, format="ICO", sizes=[(s, s) for s in ICO_SIZES])
44
+ return dest
45
+
46
+
47
+ def _open(src: Path):
48
+ image_module = _pillow()
49
+ try:
50
+ return image_module.open(src).convert("RGBA")
51
+ except OSError as exc:
52
+ raise WindowsBundleError(f"cannot read app icon {src}: {exc}") from exc
53
+
54
+
55
+ def _pillow():
56
+ try:
57
+ from PIL import Image
58
+ except ModuleNotFoundError as exc:
59
+ raise WindowsBundleError(
60
+ "generating the Windows icon needs Pillow, which is not installed.\n"
61
+ " Install the Windows extra: pip install 'kivyforge[windows]'\n"
62
+ " (or unset [tool.kivy.windows.icons].source to build without an icon)."
63
+ ) from exc
64
+ return Image
@@ -0,0 +1,193 @@
1
+ r"""Launcher: vendored-binary build + per-app bootstrap generation/placement.
2
+
3
+ This package is "everything launcher" for the Windows backend:
4
+
5
+ * the in-repo C source (``launcher.c``) and its deterministic build/vendoring
6
+ (:mod:`~kivyforge.platforms.windows.launcher.build_launcher`) — the launcher is
7
+ compiled once in CI, never on user machines, and shipped as a vendored binary;
8
+ * the per-app assembly-time pieces here in this module: generating the
9
+ ``_kivyforge_bootstrap.py`` module and placing + resource-patching a per-app
10
+ copy of the vendored launcher.
11
+
12
+ Unlike macOS/Linux (where the launcher itself is per-app), the Windows launcher
13
+ is a single prebuilt binary; everything app-specific lives in the generated
14
+ ``_kivyforge_bootstrap.py`` at the bundle root, which the launcher runs under the
15
+ bundled ``python.exe``. The bootstrap:
16
+
17
+ 1. sets the explicit **AppUserModelID** (``SetCurrentProcessExplicitAppUserModelID``
18
+ via ctypes) **before** any window, so the taskbar groups the app under its own
19
+ identity/icon;
20
+ 2. discovers ``python\share\*\bin`` **generically** (not SDL-hard-coded) and
21
+ registers each with ``os.add_dll_directory`` before ``import kivy`` (defense in
22
+ depth — the real DLL-discovery fix is those dirs existing in the prefix at all);
23
+ 3. when ``<bundle>\bin`` exists (declared native binaries, Phase 6), registers it
24
+ for DLL discovery and prepends it to ``PATH``;
25
+ 4. **retains every ``add_dll_directory`` cookie for the process lifetime** (closing
26
+ / GC-ing a cookie removes its directory from the search path);
27
+ 5. puts ``<bundle>\app`` on ``sys.path``, sets the CWD there, and **runs** the
28
+ entry-point module as ``__main__`` (via :func:`runpy.run_module`) so a
29
+ standard ``if __name__ == "__main__": App().run()`` guard fires.
30
+
31
+ Then the vendored launcher is copied to ``<safe-name>.exe`` and its icon/version
32
+ resources are patched with ``rcedit``. Numeric version fields come from the
33
+ Phase 9 PEP 440 mapping; here we set the human/string resources.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import keyword
39
+ import shutil
40
+ from pathlib import Path
41
+
42
+ from .. import WindowsBundleError
43
+ from ..assets import vendored_launcher
44
+ from ..naming import windows_safe_name
45
+ from ..rcedit import ResourcePatch, patch_resources
46
+
47
+ BOOTSTRAP_NAME = "_kivyforge_bootstrap.py"
48
+
49
+ # ``{app_id}`` and ``{entry}`` are validated (AppUserModelID / dotted module
50
+ # name) before formatting, so inlining them as repr literals is safe.
51
+ _BOOTSTRAP = '''\
52
+ # Generated by kivyforge - do not edit.
53
+ """Onedir bootstrap: DLL discovery + AppUserModelID + entry-point import."""
54
+
55
+ import os
56
+ import sys
57
+ from pathlib import Path
58
+
59
+ _BUNDLE = Path(__file__).resolve().parent
60
+ _APP_ID = {app_id!r}
61
+ _ENTRY = {entry!r}
62
+
63
+ # Retain every add_dll_directory cookie for the whole process lifetime: closing
64
+ # or garbage-collecting a cookie removes its directory from the DLL search path.
65
+ _DLL_DIR_COOKIES = []
66
+
67
+
68
+ def _register_dll_dir(path):
69
+ if path.is_dir():
70
+ _DLL_DIR_COOKIES.append(os.add_dll_directory(str(path)))
71
+
72
+
73
+ def _set_app_id(app_id):
74
+ if not app_id:
75
+ return
76
+ try:
77
+ import ctypes
78
+
79
+ ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
80
+ except Exception:
81
+ # Taskbar identity is best-effort; never block startup on it.
82
+ pass
83
+
84
+
85
+ def _bootstrap():
86
+ # (1) Taskbar identity before any window is created.
87
+ _set_app_id(_APP_ID)
88
+
89
+ # (2) Native DLL directories from the runtime prefix, discovered generically.
90
+ share = _BUNDLE / "python" / "share"
91
+ if share.is_dir():
92
+ for dep in sorted(share.iterdir()):
93
+ _register_dll_dir(dep / "bin")
94
+
95
+ # (3) Declared native binaries in <bundle>\\bin: DLL search + PATH prepend.
96
+ native_bin = _BUNDLE / "bin"
97
+ if native_bin.is_dir():
98
+ _register_dll_dir(native_bin)
99
+ os.environ["PATH"] = str(native_bin) + os.pathsep + os.environ.get("PATH", "")
100
+
101
+ # (5) App code on sys.path; CWD = app dir; run the entry module as __main__
102
+ # so a standard ``if __name__ == "__main__": App().run()`` guard fires. This
103
+ # mirrors the macOS/Linux launchers, which exec the interpreter on the entry
104
+ # script (importing it as an ordinary module would not run that block).
105
+ app_dir = _BUNDLE / "app"
106
+ sys.path.insert(0, str(app_dir))
107
+ os.chdir(app_dir)
108
+ import runpy
109
+
110
+ runpy.run_module(_ENTRY, run_name="__main__", alter_sys=True)
111
+
112
+
113
+ _bootstrap()
114
+ '''
115
+
116
+
117
+ def _validate_entry_point(entry_point: str) -> None:
118
+ parts = entry_point.split(".")
119
+ if not entry_point or not all(
120
+ p.isidentifier() and not keyword.iskeyword(p) for p in parts
121
+ ):
122
+ raise WindowsBundleError(
123
+ f"entry_point {entry_point!r} is not a valid module name."
124
+ )
125
+
126
+
127
+ def render_bootstrap(*, entry_point: str, app_id: str) -> str:
128
+ """The ``_kivyforge_bootstrap.py`` source for *entry_point* + *app_id* (pure)."""
129
+ _validate_entry_point(entry_point)
130
+ return _BOOTSTRAP.format(app_id=app_id, entry=entry_point)
131
+
132
+
133
+ def write_bootstrap(bundle: Path, *, entry_point: str, app_id: str) -> Path:
134
+ """Write ``_kivyforge_bootstrap.py`` at the *bundle* root; return its path."""
135
+ dest = bundle / BOOTSTRAP_NAME
136
+ dest.write_text(
137
+ render_bootstrap(entry_point=entry_point, app_id=app_id), encoding="utf-8"
138
+ )
139
+ return dest
140
+
141
+
142
+ def launcher_exe_name(display_name: str) -> str:
143
+ """The Windows-safe ``<name>.exe`` file name for *display_name*."""
144
+ return f"{windows_safe_name(display_name)}.exe"
145
+
146
+
147
+ def copy_launcher(
148
+ bundle: Path,
149
+ *,
150
+ display_name: str,
151
+ launcher_src: Path | None = None,
152
+ ) -> Path:
153
+ """Copy the vendored launcher to ``<bundle>\\<safe-name>.exe`` (no patch).
154
+
155
+ *launcher_src* defaults to the vendored, SHA-256-verified binary; tests
156
+ inject a fake. Returns the copied ``.exe`` path. Resource patching is a
157
+ separate step (:func:`patch_launcher`) so callers can defer it until after
158
+ the bundle is in its final location — an rcedit-patched executable is a
159
+ strong antivirus trigger that holds the file open and can block a directory
160
+ rename of the still-being-assembled tree on Windows.
161
+ """
162
+ # arm64: pick the launcher matching the target arch, i.e. thread arch into
163
+ # vendored_launcher(arch) so an arm64 bundle gets launcher-arm64.exe. The
164
+ # bundler (bundle.py) already resolves target_arch. See arm64-windows.md §5.
165
+ src = launcher_src if launcher_src is not None else vendored_launcher()
166
+ dest = bundle / launcher_exe_name(display_name)
167
+ shutil.copy2(src, dest)
168
+ return dest
169
+
170
+
171
+ def patch_launcher(
172
+ exe: Path, patch: ResourcePatch, *, rcedit: Path | None = None
173
+ ) -> None:
174
+ """Patch a placed launcher's icon/version resources in place with rcedit."""
175
+ patch_resources(exe, patch, rcedit=rcedit)
176
+
177
+
178
+ def place_launcher(
179
+ bundle: Path,
180
+ *,
181
+ display_name: str,
182
+ patch: ResourcePatch,
183
+ launcher_src: Path | None = None,
184
+ rcedit: Path | None = None,
185
+ ) -> Path:
186
+ """Copy the vendored launcher to ``<bundle>\\<safe-name>.exe`` + patch it.
187
+
188
+ Convenience wrapper over :func:`copy_launcher` + :func:`patch_launcher` for
189
+ callers that place and patch in one location. Returns the placed ``.exe``.
190
+ """
191
+ dest = copy_launcher(bundle, display_name=display_name, launcher_src=launcher_src)
192
+ patch_resources(dest, patch, rcedit=rcedit)
193
+ return dest