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,379 @@
1
+ /*
2
+ * kivyforge Windows onedir launcher (bootloader).
3
+ *
4
+ * A prebuilt, windowed-subsystem native launcher. One binary serves every app:
5
+ * everything app-specific lives in the generated bootstrap module, and the only
6
+ * per-app change to the binary is its icon/version *resources* (patched with
7
+ * rcedit at package time). See docs/design/platforms/windows/bootloader-windows.md.
8
+ *
9
+ * Behavior (spawn-and-wait, NOT exec):
10
+ * 1. Self-locate via GetModuleFileNameW (loop on ERROR_INSUFFICIENT_BUFFER;
11
+ * never a fixed MAX_PATH); derive the bundle layout from the launcher's own
12
+ * directory (never trust the CWD).
13
+ * 2. Set a Unicode child environment: PYTHONHOME=<bundle>\python,
14
+ * PYTHONPATH=<bundle>\app, PYTHONNOUSERSITE=1.
15
+ * 3. Build the child command line
16
+ * (<bundle>\python\python.exe <bundle>\_kivyforge_bootstrap.py <argv...>)
17
+ * with the correct CommandLineToArgvW-inverse quoting algorithm.
18
+ * 4. Console handoff: AttachConsole(ATTACH_PARENT_PROCESS). If a parent console
19
+ * exists (the `kivyforge run` path), inherit its std handles so the child's
20
+ * output/tracebacks reach the terminal. Otherwise (Explorer double-click),
21
+ * spawn with CREATE_NO_WINDOW so no console flashes.
22
+ * 5. CreateProcessW with CREATE_SUSPENDED, assign to a kill-on-close Job
23
+ * object, THEN ResumeThread (so no grandchild escapes the job). The job is
24
+ * best-effort: on the rare host where it cannot be armed/assigned the app
25
+ * still launches, just without the kill-on-close guarantee.
26
+ * 6. WaitForSingleObject -> GetExitCodeProcess -> exit with the child's code.
27
+ * Close every handle on every path.
28
+ *
29
+ * Linked /SUBSYSTEM:WINDOWS (windowed only; console apps are out of scope).
30
+ */
31
+
32
+ #ifndef UNICODE
33
+ #define UNICODE
34
+ #endif
35
+ #ifndef _UNICODE
36
+ #define _UNICODE
37
+ #endif
38
+
39
+ #include <windows.h>
40
+ #include <shellapi.h>
41
+ #include <stddef.h>
42
+
43
+ /* Fixed convention names (see bootloader-windows.md / windows-spec). */
44
+ static const wchar_t *PYTHON_REL = L"\\python\\python.exe";
45
+ static const wchar_t *BOOTSTRAP_REL = L"\\_kivyforge_bootstrap.py";
46
+ static const wchar_t *PYTHONHOME_REL = L"\\python";
47
+ static const wchar_t *PYTHONPATH_REL = L"\\app";
48
+
49
+ /* Exit code for a launcher-internal failure (distinct from any app exit code).
50
+ * 71 == EX_OSERR, matching the macOS/Linux launchers. */
51
+ #define LAUNCHER_EX_OSERR 71
52
+
53
+ /* ----------------------------------------------------------------------------
54
+ * Small heap helpers. On OOM we fail hard with the OS-error exit code; there is
55
+ * no meaningful recovery for a launcher.
56
+ * ------------------------------------------------------------------------- */
57
+ static void *xalloc(size_t bytes) {
58
+ void *p = HeapAlloc(GetProcessHeap(), 0, bytes);
59
+ return p;
60
+ }
61
+
62
+ static void xfree(void *p) {
63
+ if (p != NULL) {
64
+ HeapFree(GetProcessHeap(), 0, p);
65
+ }
66
+ }
67
+
68
+ /* ----------------------------------------------------------------------------
69
+ * Self-location: GetModuleFileNameW into a growable buffer. Returns a
70
+ * heap-allocated wide string (caller frees) or NULL on failure.
71
+ * ------------------------------------------------------------------------- */
72
+ static wchar_t *module_path(void) {
73
+ DWORD cap = MAX_PATH;
74
+ for (;;) {
75
+ wchar_t *buf = (wchar_t *)xalloc(cap * sizeof(wchar_t));
76
+ if (buf == NULL) {
77
+ return NULL;
78
+ }
79
+ DWORD n = GetModuleFileNameW(NULL, buf, cap);
80
+ if (n == 0) {
81
+ xfree(buf);
82
+ return NULL;
83
+ }
84
+ if (n < cap && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
85
+ /* n < cap means the name fit (and is NUL-terminated). */
86
+ return buf;
87
+ }
88
+ xfree(buf);
89
+ if (cap > (1 << 20)) {
90
+ /* Runaway guard: no sane path is a million wchars. */
91
+ return NULL;
92
+ }
93
+ cap *= 2;
94
+ }
95
+ }
96
+
97
+ /* Return the length of *path up to (not including) the final backslash — i.e.
98
+ * the directory portion. If there is no backslash, returns 0. */
99
+ static size_t dir_len(const wchar_t *path) {
100
+ size_t last = 0;
101
+ for (size_t i = 0; path[i] != L'\0'; i++) {
102
+ if (path[i] == L'\\') {
103
+ last = i;
104
+ }
105
+ }
106
+ return last;
107
+ }
108
+
109
+ /* Concatenate dir[0..dir_count) + suffix into a fresh heap string. */
110
+ static wchar_t *join(const wchar_t *dir, size_t dir_count, const wchar_t *suffix) {
111
+ size_t slen = 0;
112
+ while (suffix[slen] != L'\0') {
113
+ slen++;
114
+ }
115
+ wchar_t *out = (wchar_t *)xalloc((dir_count + slen + 1) * sizeof(wchar_t));
116
+ if (out == NULL) {
117
+ return NULL;
118
+ }
119
+ for (size_t i = 0; i < dir_count; i++) {
120
+ out[i] = dir[i];
121
+ }
122
+ for (size_t i = 0; i < slen; i++) {
123
+ out[dir_count + i] = suffix[i];
124
+ }
125
+ out[dir_count + slen] = L'\0';
126
+ return out;
127
+ }
128
+
129
+ /* ----------------------------------------------------------------------------
130
+ * Argv quoting — the exact inverse of CommandLineToArgvW (Daniel Colascione's
131
+ * documented algorithm). Naive double-quoting corrupts paths ending in `\` and
132
+ * any argument containing a `"`. Appends the quoted form of *arg to the buffer.
133
+ * ------------------------------------------------------------------------- */
134
+
135
+ typedef struct {
136
+ wchar_t *data;
137
+ size_t len;
138
+ size_t cap;
139
+ int oom;
140
+ } WBuf;
141
+
142
+ static void wbuf_init(WBuf *b) {
143
+ b->cap = 256;
144
+ b->len = 0;
145
+ b->oom = 0;
146
+ b->data = (wchar_t *)xalloc(b->cap * sizeof(wchar_t));
147
+ if (b->data == NULL) {
148
+ b->oom = 1;
149
+ }
150
+ }
151
+
152
+ static void wbuf_push(WBuf *b, wchar_t c) {
153
+ if (b->oom) {
154
+ return;
155
+ }
156
+ if (b->len + 1 >= b->cap) {
157
+ size_t ncap = b->cap * 2;
158
+ wchar_t *nd = (wchar_t *)xalloc(ncap * sizeof(wchar_t));
159
+ if (nd == NULL) {
160
+ b->oom = 1;
161
+ return;
162
+ }
163
+ for (size_t i = 0; i < b->len; i++) {
164
+ nd[i] = b->data[i];
165
+ }
166
+ xfree(b->data);
167
+ b->data = nd;
168
+ b->cap = ncap;
169
+ }
170
+ b->data[b->len++] = c;
171
+ }
172
+
173
+ static void wbuf_push_str(WBuf *b, const wchar_t *s) {
174
+ for (size_t i = 0; s[i] != L'\0'; i++) {
175
+ wbuf_push(b, s[i]);
176
+ }
177
+ }
178
+
179
+ /* Append *arg, quoted iff necessary, to *b (per CommandLineToArgvW rules). */
180
+ static void append_quoted(WBuf *b, const wchar_t *arg) {
181
+ int needs_quotes = (arg[0] == L'\0');
182
+ for (size_t i = 0; arg[i] != L'\0'; i++) {
183
+ if (arg[i] == L' ' || arg[i] == L'\t' || arg[i] == L'"') {
184
+ needs_quotes = 1;
185
+ break;
186
+ }
187
+ }
188
+ if (!needs_quotes) {
189
+ wbuf_push_str(b, arg);
190
+ return;
191
+ }
192
+ wbuf_push(b, L'"');
193
+ for (size_t i = 0;; i++) {
194
+ size_t backslashes = 0;
195
+ while (arg[i] == L'\\') {
196
+ i++;
197
+ backslashes++;
198
+ }
199
+ if (arg[i] == L'\0') {
200
+ /* Escape all backslashes, but let the terminating quote be added
201
+ * after; the backslashes must not eat that quote. */
202
+ for (size_t k = 0; k < backslashes * 2; k++) {
203
+ wbuf_push(b, L'\\');
204
+ }
205
+ break;
206
+ } else if (arg[i] == L'"') {
207
+ /* Escape all backslashes and the following quote. */
208
+ for (size_t k = 0; k < backslashes * 2 + 1; k++) {
209
+ wbuf_push(b, L'\\');
210
+ }
211
+ wbuf_push(b, arg[i]);
212
+ } else {
213
+ /* Backslashes are not special here. */
214
+ for (size_t k = 0; k < backslashes; k++) {
215
+ wbuf_push(b, L'\\');
216
+ }
217
+ wbuf_push(b, arg[i]);
218
+ }
219
+ }
220
+ wbuf_push(b, L'"');
221
+ }
222
+
223
+ /* ----------------------------------------------------------------------------
224
+ * Entry point. /SUBSYSTEM:WINDOWS with the Unicode CRT startup calls wWinMain.
225
+ * We ignore its args and pull the real command line via GetCommandLineW so we
226
+ * get wide-char argv regardless.
227
+ * ------------------------------------------------------------------------- */
228
+ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
229
+ LPWSTR lpCmdLine, int nCmdShow) {
230
+ (void)hInstance;
231
+ (void)hPrevInstance;
232
+ (void)lpCmdLine;
233
+ (void)nCmdShow;
234
+
235
+ wchar_t *exe = module_path();
236
+ if (exe == NULL) {
237
+ return LAUNCHER_EX_OSERR;
238
+ }
239
+ size_t bundle_len = dir_len(exe); /* index of the last backslash */
240
+
241
+ wchar_t *python = join(exe, bundle_len, PYTHON_REL);
242
+ wchar_t *bootstrap = join(exe, bundle_len, BOOTSTRAP_REL);
243
+ wchar_t *pyhome = join(exe, bundle_len, PYTHONHOME_REL);
244
+ wchar_t *pypath = join(exe, bundle_len, PYTHONPATH_REL);
245
+ if (python == NULL || bootstrap == NULL || pyhome == NULL || pypath == NULL) {
246
+ return LAUNCHER_EX_OSERR;
247
+ }
248
+
249
+ /* Child environment: inherit ours, then override the Python isolation vars.
250
+ * SetEnvironmentVariableW mutates this process's block, which the child
251
+ * inherits (we pass a NULL environment to CreateProcessW). */
252
+ SetEnvironmentVariableW(L"PYTHONHOME", pyhome);
253
+ SetEnvironmentVariableW(L"PYTHONPATH", pypath);
254
+ SetEnvironmentVariableW(L"PYTHONNOUSERSITE", L"1");
255
+
256
+ /* Reconstruct the child command line: python.exe, the bootstrap, then every
257
+ * forwarded argument (argv[1..]) quoted correctly. */
258
+ int argc = 0;
259
+ LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
260
+ if (argv == NULL) {
261
+ return LAUNCHER_EX_OSERR;
262
+ }
263
+ WBuf cmd;
264
+ wbuf_init(&cmd);
265
+ append_quoted(&cmd, python);
266
+ wbuf_push(&cmd, L' ');
267
+ append_quoted(&cmd, bootstrap);
268
+ for (int i = 1; i < argc; i++) {
269
+ wbuf_push(&cmd, L' ');
270
+ append_quoted(&cmd, argv[i]);
271
+ }
272
+ wbuf_push(&cmd, L'\0');
273
+ LocalFree(argv);
274
+ if (cmd.oom) {
275
+ return LAUNCHER_EX_OSERR;
276
+ }
277
+
278
+ /* Console handoff. A windowed-subsystem process does not reliably inherit a
279
+ * parent console's std handles; make the handoff explicit. */
280
+ BOOL attached = AttachConsole(ATTACH_PARENT_PROCESS);
281
+ DWORD creation_flags = CREATE_SUSPENDED;
282
+ STARTUPINFOW si;
283
+ ZeroMemory(&si, sizeof(si));
284
+ si.cb = sizeof(si);
285
+ BOOL inherit_handles = FALSE;
286
+ if (attached) {
287
+ /* Pass the attached terminal's std handles to the console-subsystem
288
+ * child so prints/tracebacks land where the developer is looking. */
289
+ HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
290
+ HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
291
+ HANDLE herr = GetStdHandle(STD_ERROR_HANDLE);
292
+ si.dwFlags |= STARTF_USESTDHANDLES;
293
+ si.hStdInput = hin;
294
+ si.hStdOutput = hout;
295
+ si.hStdError = herr;
296
+ SetHandleInformation(hin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
297
+ SetHandleInformation(hout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
298
+ SetHandleInformation(herr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
299
+ inherit_handles = TRUE;
300
+ } else {
301
+ /* No parent console (Explorer double-click): suppress the console flash
302
+ * that a console-subsystem python.exe would otherwise create.
303
+ *
304
+ * Why CREATE_NO_WINDOW on python.exe rather than launching pythonw.exe:
305
+ * a single code path serves both entry points. On the `kivyforge run`
306
+ * path we WANT the console-subsystem python.exe so its stdout/stderr and
307
+ * tracebacks attach to the parent terminal (above). Using pythonw.exe for
308
+ * double-click would mean detecting the mode and choosing a different
309
+ * child, and pythonw.exe forfeits that attach-for-diagnostics story. One
310
+ * child + CREATE_NO_WINDOW gives silent double-click AND visible `run`. */
311
+ creation_flags |= CREATE_NO_WINDOW;
312
+ }
313
+
314
+ /* A Job object with kill-on-close makes the whole child process tree die
315
+ * with the launcher (Ctrl-C, task kill, crash) — no orphaned python. This
316
+ * is best-effort: if the job cannot be established (rare on Win8+ where
317
+ * nested jobs are the default), we drop it and still launch rather than
318
+ * refuse to start the app. Failing calls are checked, never ignored. */
319
+ HANDLE job = CreateJobObjectW(NULL, NULL);
320
+ if (job != NULL) {
321
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
322
+ ZeroMemory(&jeli, sizeof(jeli));
323
+ jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
324
+ if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &jeli,
325
+ sizeof(jeli))) {
326
+ /* A job we cannot arm for kill-on-close is worse than none: close it
327
+ * so we do not rely on a half-configured job below. */
328
+ CloseHandle(job);
329
+ job = NULL;
330
+ }
331
+ }
332
+
333
+ PROCESS_INFORMATION pi;
334
+ ZeroMemory(&pi, sizeof(pi));
335
+ BOOL ok = CreateProcessW(python, /* application: the bundled python */
336
+ cmd.data, /* full command line */
337
+ NULL, NULL, /* default security */
338
+ inherit_handles,
339
+ creation_flags,
340
+ NULL, /* inherit our (mutated) environment */
341
+ NULL, /* inherit CWD; bootstrap sets its own */
342
+ &si, &pi);
343
+ if (!ok) {
344
+ if (job != NULL) {
345
+ CloseHandle(job);
346
+ }
347
+ return LAUNCHER_EX_OSERR;
348
+ }
349
+
350
+ /* Assign to the job while still suspended (closes the grandchild-escape
351
+ * race), THEN resume the main thread. If assignment fails we cannot promise
352
+ * kill-on-close, so drop the job rather than pretend the guarantee holds. */
353
+ if (job != NULL && !AssignProcessToJobObject(job, pi.hProcess)) {
354
+ CloseHandle(job);
355
+ job = NULL;
356
+ }
357
+ if (ResumeThread(pi.hThread) == (DWORD)-1) {
358
+ /* The child is stuck suspended; never WaitForSingleObject forever on it.
359
+ * Tear it down deterministically and report an OS error. */
360
+ TerminateProcess(pi.hProcess, LAUNCHER_EX_OSERR);
361
+ CloseHandle(pi.hThread);
362
+ CloseHandle(pi.hProcess);
363
+ if (job != NULL) {
364
+ CloseHandle(job);
365
+ }
366
+ return LAUNCHER_EX_OSERR;
367
+ }
368
+
369
+ WaitForSingleObject(pi.hProcess, INFINITE);
370
+ DWORD code = LAUNCHER_EX_OSERR;
371
+ GetExitCodeProcess(pi.hProcess, &code);
372
+
373
+ CloseHandle(pi.hThread);
374
+ CloseHandle(pi.hProcess);
375
+ if (job != NULL) {
376
+ CloseHandle(job);
377
+ }
378
+ return (int)code;
379
+ }
@@ -0,0 +1,112 @@
1
+ """Windows lock stack (``pylock.windows.toml``).
2
+
3
+ A thin Windows *profile* (:class:`WindowsProfile`) over the generic
4
+ wheel+runtime lock engine (``kivyforge.lock.wheelruntime``). This module is the
5
+ stable Windows public surface: it binds the profile into the core and re-exports
6
+ the engine's types under Windows-friendly names so callers (the CLI, tests)
7
+ never reach into the generic package directly.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Callable
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+
16
+ from kivyforge.config.model import Config
17
+ from kivyforge.lock.wheelruntime import (
18
+ PythonRuntime as WindowsPythonRuntime,
19
+ )
20
+ from kivyforge.lock.wheelruntime import (
21
+ ResolvedPackage,
22
+ ResolvedWheel,
23
+ RuntimeArtifact,
24
+ RuntimeProvider,
25
+ RuntimeProviderError,
26
+ Variant,
27
+ WheelResolver,
28
+ WheelResolverError,
29
+ WheelRuntimeBuildError,
30
+ WheelRuntimeLock,
31
+ build_wheel_runtime_lock,
32
+ diff_summary,
33
+ get_wheel_resolver,
34
+ semantic_equal,
35
+ )
36
+ from kivyforge.lock.wheelruntime import serialize as _ser
37
+
38
+ from .profile import WindowsProfile, wheel_arch, windows_platform_tag
39
+ from .runtime import PythonBuildStandaloneProvider
40
+
41
+ # Windows-friendly aliases for the generic engine types.
42
+ WindowsLockfile = WheelRuntimeLock
43
+ WindowsBuildError = WheelRuntimeBuildError
44
+ WindowsResolverError = WheelResolverError
45
+
46
+ _PROFILE = WindowsProfile()
47
+
48
+ __all__ = [
49
+ "PythonBuildStandaloneProvider",
50
+ "ResolvedPackage",
51
+ "ResolvedWheel",
52
+ "RuntimeArtifact",
53
+ "RuntimeProvider",
54
+ "RuntimeProviderError",
55
+ "Variant",
56
+ "WheelResolver",
57
+ "WindowsBuildError",
58
+ "WindowsLockfile",
59
+ "WindowsProfile",
60
+ "WindowsPythonRuntime",
61
+ "WindowsResolverError",
62
+ "build_windows_lockfile",
63
+ "diff_summary",
64
+ "dumps",
65
+ "get_windows_resolver",
66
+ "load",
67
+ "loads",
68
+ "semantic_equal",
69
+ "wheel_arch",
70
+ "windows_platform_tag",
71
+ ]
72
+
73
+
74
+ def build_windows_lockfile(
75
+ config: Config,
76
+ pyproject_text: str,
77
+ *,
78
+ project_root: Path | None = None,
79
+ resolver: WheelResolver | None = None,
80
+ runtime_provider: RuntimeProvider | None = None,
81
+ offline: bool = False,
82
+ now: datetime | None = None,
83
+ on_warning: Callable[[str], None] | None = None,
84
+ ) -> WindowsLockfile:
85
+ return build_wheel_runtime_lock(
86
+ _PROFILE,
87
+ config,
88
+ pyproject_text,
89
+ project_root=project_root,
90
+ resolver=resolver,
91
+ runtime_provider=runtime_provider,
92
+ native_binaries=config.windows.binaries if config.windows else (),
93
+ offline=offline,
94
+ now=now,
95
+ on_warning=on_warning,
96
+ )
97
+
98
+
99
+ def dumps(lock: WindowsLockfile) -> str:
100
+ return _ser.dumps(lock)
101
+
102
+
103
+ def loads(text: str) -> WindowsLockfile:
104
+ return _ser.loads(text, platform="windows")
105
+
106
+
107
+ def load(path: str | Path) -> WindowsLockfile:
108
+ return _ser.load(path, platform="windows")
109
+
110
+
111
+ def get_windows_resolver(backend: str = "pip", **kwargs) -> WheelResolver:
112
+ return get_wheel_resolver(backend, **kwargs)
@@ -0,0 +1,101 @@
1
+ """Windows lock profile — the platform-specific inputs to the wheel+runtime core.
2
+
3
+ The simplest of the desktop family: Windows has a single stable wheel platform
4
+ tag per arch (``win_amd64`` this phase — ``win32``/``win_arm64`` are out of
5
+ scope), so there is no tag ladder (Linux) and no fat-wheel arch (macOS
6
+ ``universal2``). ``kivy_deps.*`` are ordinary wheels — Kivy's ``win_amd64`` wheel
7
+ declares them as conditional dependencies, so they flow through the shared
8
+ resolver with URL + SHA-256 pins with no special-casing. The bundled runtime
9
+ comes from python-build-standalone (``x86_64-pc-windows-msvc``). Everything else
10
+ — resolve/serialize/build orchestration — is the shared core.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from kivyforge.config.errors import ConfigError
16
+ from kivyforge.config.model import Config
17
+ from kivyforge.lock.wheelruntime.profile import PlatformLockProfile
18
+ from kivyforge.lock.wheelruntime.resolver import Variant
19
+ from kivyforge.lock.wheelruntime.runtime import RuntimeProvider
20
+
21
+ from .runtime import PythonBuildStandaloneProvider
22
+
23
+ # arm64: add "arm64" here and "arm64": "win_arm64" to _ARCH_TO_TAG; _TAG_TO_ARCH
24
+ # derives automatically, and coverage_error's "win_amd64" text should generalize
25
+ # to the per-arch tag. Gated on upstream win_arm64 wheels — see arm64-windows.md §2.
26
+ VALID_WHEEL_ARCHS = frozenset({"amd64"})
27
+
28
+ # kivyforge arch name -> the wheel platform tag pip requests / a wheel carries.
29
+ _ARCH_TO_TAG = {"amd64": "win_amd64"}
30
+ _TAG_TO_ARCH = {tag: arch for arch, tag in _ARCH_TO_TAG.items()}
31
+
32
+
33
+ def windows_platform_tag(arch: str) -> str:
34
+ """The pip ``--platform`` tag for a Windows arch (``amd64`` -> ``win_amd64``)."""
35
+ return _ARCH_TO_TAG[arch]
36
+
37
+
38
+ def wheel_arch(platform_tag: str) -> str | None:
39
+ """The architecture a Windows wheel platform tag targets, or ``None``.
40
+
41
+ ``win_amd64`` -> ``amd64``. ``win32`` (x86) and ``win_arm64`` are recognized
42
+ as Windows tags but are unsupported this phase, so they resolve to their arch
43
+ name only if that arch is a valid target (they are not), i.e. ``None`` here.
44
+ A non-Windows tag (``any`` is handled by the shared core) also returns
45
+ ``None``.
46
+ """
47
+ return _TAG_TO_ARCH.get(platform_tag)
48
+
49
+
50
+ class WindowsProfile(PlatformLockProfile):
51
+ platform = "windows"
52
+
53
+ def overlay(self, config: Config):
54
+ return config.windows
55
+
56
+ def missing_overlay_error(self) -> str:
57
+ return "pyproject.toml has no [tool.kivy.windows] table; nothing to lock."
58
+
59
+ def python_version(self, config: Config) -> str:
60
+ # [tool.kivy.windows.python].version is required (the loader raises a
61
+ # ConfigError when it is missing), so it is always set here. Never
62
+ # silently substitute a hidden default — that would pin an unexpected
63
+ # (and possibly unreleased) Python instead of surfacing the misconfig.
64
+ version = config.windows_required.python_version
65
+ if not version:
66
+ raise ConfigError(
67
+ "missing required [tool.kivy.windows.python].version",
68
+ key_path="tool.kivy.windows.python.version",
69
+ )
70
+ return version
71
+
72
+ def variants(self, config: Config) -> tuple[Variant, ...]:
73
+ return tuple(
74
+ Variant(arch=arch, platform_tag=windows_platform_tag(arch))
75
+ for arch in config.windows_required.archs
76
+ )
77
+
78
+ def wheel_covers(self, platform_tag: str, archs: tuple[str, ...]) -> set[str]:
79
+ arch = wheel_arch(platform_tag)
80
+ if arch in archs:
81
+ return {arch}
82
+ return set()
83
+
84
+ def runtime_provider(self, config: Config) -> RuntimeProvider:
85
+ return PythonBuildStandaloneProvider()
86
+
87
+ def coverage_error(self, name: str, missing: list[str]) -> str:
88
+ return (
89
+ f"{name} has no win_amd64 wheel for arch(es): {', '.join(missing)}.\n"
90
+ f" A compiled package must publish a win_amd64 wheel to be locked "
91
+ f"reproducibly.\n"
92
+ f" Supply the wheel via extra_index_urls/find_links, or drop the "
93
+ f"dependency on Windows."
94
+ )
95
+
96
+ def wheel_scope_hint(self) -> str:
97
+ return (
98
+ "Vendored wheels must live under the project directory, a sibling "
99
+ "directory, or the enclosing repository (e.g. a wheels/ directory "
100
+ "beside pyproject.toml)."
101
+ )
@@ -0,0 +1,64 @@
1
+ """Windows runtime provider — python-build-standalone with the msvc triple.
2
+
3
+ A one-line specialization of the generic :class:`PbsProvider`: it fixes the
4
+ arch→target-triple map to Windows's ``x86_64-pc-windows-msvc`` build, and
5
+ otherwise inherits the shared PBS resolution + injectable metadata fetcher. The
6
+ Windows ``install_only`` archive normalizes under the same top-level ``python/``
7
+ tree as the darwin/linux ones (a normal relocatable prefix — ``python.exe`` at
8
+ the root, ``Lib/site-packages`` beneath it), which is load-bearing: staging must
9
+ **not** flatten, prune, or ``._pth``-isolate it (windows-spec).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from kivyforge.lock.wheelruntime.runtime import (
15
+ MetadataFetcher,
16
+ PbsProvider,
17
+ ReleaseAsset,
18
+ RuntimeProvider,
19
+ RuntimeProviderError,
20
+ pbs_asset_glob,
21
+ )
22
+
23
+ __all__ = [
24
+ "PythonBuildStandaloneProvider",
25
+ "ReleaseAsset",
26
+ "RuntimeProvider",
27
+ "RuntimeProviderError",
28
+ "WINDOWS_TRIPLES",
29
+ "get_runtime_provider",
30
+ "pbs_asset_name",
31
+ ]
32
+
33
+ # kivyforge arch name -> PBS/LLVM Windows target triple.
34
+ # arm64: add "arm64": "aarch64-pc-windows-msvc" (confirm PBS ships that
35
+ # install_only archive for the pinned CPython). The generic PbsProvider handles
36
+ # the rest — no other change here. See arm64-windows.md §3.
37
+ WINDOWS_TRIPLES = {
38
+ "amd64": "x86_64-pc-windows-msvc",
39
+ }
40
+
41
+ # Windows-facing alias for the generic PBS asset glob helper.
42
+ pbs_asset_name = pbs_asset_glob
43
+
44
+
45
+ class PythonBuildStandaloneProvider(PbsProvider):
46
+ """PBS provider fixed to the Windows msvc triple."""
47
+
48
+ def __init__(
49
+ self,
50
+ metadata_fetcher: MetadataFetcher | None = None,
51
+ *,
52
+ floor: str | None = None,
53
+ ) -> None:
54
+ super().__init__(WINDOWS_TRIPLES, metadata_fetcher, floor=floor)
55
+
56
+
57
+ def get_runtime_provider(
58
+ name: str = "python-build-standalone", **kwargs
59
+ ) -> RuntimeProvider:
60
+ if name in ("python-build-standalone", "pbs", "python_build_standalone"):
61
+ return PythonBuildStandaloneProvider(**kwargs)
62
+ raise RuntimeProviderError(
63
+ f"unknown runtime provider {name!r} (expected python-build-standalone)"
64
+ )