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,107 @@
1
+ """The AppDir ``AppRun`` launcher — a POSIX shell script (linux-spec).
2
+
3
+ AppImage does not require an ELF ``AppRun``, and Linux has no analog of the
4
+ Finder/LaunchServices problem that forced the compiled Mach-O stub on macOS, so
5
+ the launcher is a small shell script. It resolves its own directory (so the
6
+ AppDir stays relocatable), points the bundled CPython at its own home + the
7
+ app's ``lib``/``app`` directories, sets the SDL WM_CLASS so the window groups
8
+ under the ``.desktop``'s ``StartupWMClass``, and ``exec``s the interpreter on
9
+ the entry-point module.
10
+
11
+ By default there is deliberately **no ``LD_LIBRARY_PATH``**: PBS rpaths are
12
+ ``$ORIGIN``-relative and manylinux wheels vendor their own native libs
13
+ (auditwheel), so the host's libGL/libEGL are found on the normal loader path.
14
+ The one exception is when the app declares ``[tool.kivy.linux.native.binaries]``:
15
+ then ``usr/bin`` is *prepended* to ``PATH`` (helpers win by name) and *appended*
16
+ to ``LD_LIBRARY_PATH`` (declared libs load by soname but never shadow the host's
17
+ or the wheels' own ``.so``s — decided Option B in linux-spec). The
18
+ PATH-prepend / LD_LIBRARY_PATH-append asymmetry is deliberate: declared tools
19
+ win; declared libs never shadow the host.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import keyword
25
+ from pathlib import Path
26
+
27
+ from . import AppDirError
28
+
29
+ # ``{entry}`` is a ``/``-joined module path (a dotted ``entry_point`` such as
30
+ # ``pkg.start`` maps to ``pkg/start``); ``{app_id}`` is a validated ``.desktop``
31
+ # basename (letters/digits/hyphen/period) — both safe to inline.
32
+ _APPRUN = """\
33
+ #!/bin/sh
34
+ # Generated by kivyforge — do not edit.
35
+ HERE="$(dirname "$(readlink -f "$0")")"
36
+
37
+ export PYTHONHOME="$HERE/usr/python"
38
+ export PYTHONPATH="$HERE/usr/app:$HERE/usr/lib"
39
+ export PYTHONNOUSERSITE=1
40
+ {native_block}
41
+ # Match the window's WM_CLASS to the .desktop StartupWMClass so the app groups
42
+ # under its own icon on both X11 and Wayland.
43
+ export SDL_VIDEO_X11_WMCLASS="{app_id}"
44
+ export SDL_VIDEO_WAYLAND_WMCLASS="{app_id}"
45
+
46
+ exec "$HERE/usr/python/bin/python3" "$HERE/usr/app/{entry}.py" "$@"
47
+ """
48
+
49
+ # Only emitted when the app declares native binaries (no dead env vars
50
+ # otherwise — symmetry with the "no empty bin/" rule). PATH is prepended so a
51
+ # bundled helper wins by name; LD_LIBRARY_PATH is appended so the host's and the
52
+ # wheels' auditwheel-vendored .so's still win the search and a declared lib is
53
+ # only a last resort (decided Option B, linux-spec).
54
+ _NATIVE_BINARIES_BLOCK = """
55
+ # Declared native binaries (see linux-spec): helpers by name, libs by soname.
56
+ export PATH="$HERE/usr/bin:$PATH"
57
+ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$HERE/usr/bin"
58
+ """
59
+
60
+
61
+ def entry_point_rel_path(entry_point: str) -> str:
62
+ """Map a (possibly dotted) ``entry_point`` to its ``/``-joined module path.
63
+
64
+ ``"main"`` -> ``"main"``; ``"pkg.start"`` -> ``"pkg/start"`` (i.e.
65
+ ``usr/app/pkg/start.py``), matching the ``app_dir``/``entry_point`` layout
66
+ contract in the shared pyproject spec.
67
+ """
68
+ return "/".join(entry_point.split("."))
69
+
70
+
71
+ def render_apprun(
72
+ *, entry_point: str, app_id: str, has_native_binaries: bool = False
73
+ ) -> str:
74
+ """The ``AppRun`` shell source for *entry_point* + *app_id* (pure/testable).
75
+
76
+ *entry_point* may be a dotted module name (e.g. ``pkg.start``); each segment
77
+ must be a valid, non-keyword Python identifier. When *has_native_binaries*
78
+ is set, ``usr/bin`` is prepended to ``PATH`` and appended to
79
+ ``LD_LIBRARY_PATH``; otherwise the block is omitted entirely.
80
+ """
81
+ parts = entry_point.split(".")
82
+ if not entry_point or not all(
83
+ p.isidentifier() and not keyword.iskeyword(p) for p in parts
84
+ ):
85
+ raise AppDirError(f"entry_point {entry_point!r} is not a valid module name.")
86
+ native_block = _NATIVE_BINARIES_BLOCK if has_native_binaries else ""
87
+ return _APPRUN.format(
88
+ entry=entry_point_rel_path(entry_point),
89
+ app_id=app_id,
90
+ native_block=native_block,
91
+ )
92
+
93
+
94
+ def build_apprun(
95
+ dest: Path, *, entry_point: str, app_id: str, has_native_binaries: bool = False
96
+ ) -> None:
97
+ """Write an executable ``AppRun`` at *dest*."""
98
+ dest.parent.mkdir(parents=True, exist_ok=True)
99
+ dest.write_text(
100
+ render_apprun(
101
+ entry_point=entry_point,
102
+ app_id=app_id,
103
+ has_native_binaries=has_native_binaries,
104
+ ),
105
+ "utf-8",
106
+ )
107
+ dest.chmod(0o755)
@@ -0,0 +1,126 @@
1
+ """Linux lock stack (``pylock.linux.toml``).
2
+
3
+ A thin Linux *profile* (:class:`LinuxProfile`) over the generic wheel+runtime
4
+ lock engine (``kivyforge.lock.wheelruntime``). This module is the stable Linux
5
+ public surface: it binds the profile into the core and re-exports the engine's
6
+ types under Linux-friendly names so callers (the CLI, tests) never reach into
7
+ 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 LinuxPythonRuntime,
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 (
39
+ LinuxProfile,
40
+ effective_glibc_floor,
41
+ is_plain_linux_tag,
42
+ linux_wheel_arch,
43
+ linux_wheel_coverage,
44
+ manylinux_platform_tags,
45
+ plain_linux_wheel_warning,
46
+ wheel_glibc_level,
47
+ )
48
+ from .runtime import PythonBuildStandaloneProvider
49
+
50
+ # Linux-friendly aliases for the generic engine types.
51
+ LinuxLockfile = WheelRuntimeLock
52
+ LinuxBuildError = WheelRuntimeBuildError
53
+ LinuxResolverError = WheelResolverError
54
+
55
+ _PROFILE = LinuxProfile()
56
+
57
+ __all__ = [
58
+ "LinuxBuildError",
59
+ "LinuxLockfile",
60
+ "LinuxProfile",
61
+ "LinuxPythonRuntime",
62
+ "LinuxResolverError",
63
+ "PythonBuildStandaloneProvider",
64
+ "ResolvedPackage",
65
+ "ResolvedWheel",
66
+ "RuntimeArtifact",
67
+ "RuntimeProvider",
68
+ "RuntimeProviderError",
69
+ "Variant",
70
+ "WheelResolver",
71
+ "build_linux_lockfile",
72
+ "diff_summary",
73
+ "dumps",
74
+ "effective_glibc_floor",
75
+ "get_linux_resolver",
76
+ "is_plain_linux_tag",
77
+ "linux_wheel_arch",
78
+ "linux_wheel_coverage",
79
+ "load",
80
+ "loads",
81
+ "manylinux_platform_tags",
82
+ "plain_linux_wheel_warning",
83
+ "semantic_equal",
84
+ "wheel_glibc_level",
85
+ ]
86
+
87
+
88
+ def build_linux_lockfile(
89
+ config: Config,
90
+ pyproject_text: str,
91
+ *,
92
+ project_root: Path | None = None,
93
+ resolver: WheelResolver | None = None,
94
+ runtime_provider: RuntimeProvider | None = None,
95
+ offline: bool = False,
96
+ now: datetime | None = None,
97
+ on_warning: Callable[[str], None] | None = None,
98
+ ) -> LinuxLockfile:
99
+ return build_wheel_runtime_lock(
100
+ _PROFILE,
101
+ config,
102
+ pyproject_text,
103
+ project_root=project_root,
104
+ resolver=resolver,
105
+ runtime_provider=runtime_provider,
106
+ native_binaries=config.linux.binaries if config.linux else (),
107
+ offline=offline,
108
+ now=now,
109
+ on_warning=on_warning,
110
+ )
111
+
112
+
113
+ def dumps(lock: LinuxLockfile) -> str:
114
+ return _ser.dumps(lock)
115
+
116
+
117
+ def loads(text: str) -> LinuxLockfile:
118
+ return _ser.loads(text, platform="linux")
119
+
120
+
121
+ def load(path: str | Path) -> LinuxLockfile:
122
+ return _ser.load(path, platform="linux")
123
+
124
+
125
+ def get_linux_resolver(backend: str = "pip", **kwargs) -> WheelResolver:
126
+ return get_wheel_resolver(backend, **kwargs)
@@ -0,0 +1,254 @@
1
+ """Linux lock profile — the platform-specific inputs to the wheel+runtime core.
2
+
3
+ Linux variants are CPU architectures (``x86_64`` only this phase). Wheels use
4
+ manylinux platform tags; because pip does not expand the manylinux hierarchy
5
+ from a single explicit ``--platform`` (linux-spec spike), each variant requests
6
+ the full ladder of compatible tags at or below the glibc floor in one pip
7
+ invocation. The bundled runtime comes from python-build-standalone (linux-gnu).
8
+ Everything else — resolve/serialize/build orchestration — is the shared core.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+
15
+ from kivyforge.config.errors import ConfigError
16
+ from kivyforge.config.model import Config
17
+ from kivyforge.lock.wheelruntime.model import WheelRuntimeLock
18
+ from kivyforge.lock.wheelruntime.profile import PlatformLockProfile
19
+ from kivyforge.lock.wheelruntime.resolver import Variant
20
+ from kivyforge.lock.wheelruntime.runtime import RuntimeProvider
21
+
22
+ from .runtime import DEFAULT_GLIBC_FLOOR, PythonBuildStandaloneProvider
23
+
24
+ VALID_WHEEL_ARCHS = frozenset({"x86_64"})
25
+
26
+ # Legacy manylinux aliases and the perennial (PEP 600) glibc minor they map to.
27
+ _LEGACY_ALIASES = {5: "manylinux1", 12: "manylinux2010", 17: "manylinux2014"}
28
+ # manylinux1 (glibc 2.5) is the oldest tag; nothing predates it.
29
+ _MIN_MANYLINUX_MINOR = 5
30
+
31
+ _PERENNIAL_RE = re.compile(r"^manylinux_(\d+)_(\d+)_(?P<arch>.+)$")
32
+ _LINUX_TAG_RE = re.compile(
33
+ r"^(?:linux|manylinux1|manylinux2010|manylinux2014|manylinux_\d+_\d+)"
34
+ r"_(?P<arch>.+)$"
35
+ )
36
+
37
+
38
+ def _floor_parts(floor: str) -> tuple[int, int]:
39
+ """``"2.17"`` -> ``(2, 17)``; tolerant of extra/absent components."""
40
+ nums = [int(p) for p in re.findall(r"\d+", floor)]
41
+ major = nums[0] if nums else 2
42
+ minor = nums[1] if len(nums) > 1 else 0
43
+ return major, minor
44
+
45
+
46
+ def manylinux_platform_tags(floor: str, arch: str) -> tuple[str, ...]:
47
+ """The pip ``--platform`` tags to request for a glibc *floor* + *arch*.
48
+
49
+ Returns the perennial ladder from the floor down to manylinux1 (glibc 2.5),
50
+ plus the legacy aliases (``manylinux1``/``manylinux2010``/``manylinux2014``)
51
+ at or below the floor. The floor tag is first (the variant's identity tag).
52
+ A wheel matching *any* of these runs on a host meeting the floor, so pip —
53
+ given all tags in one invocation — locks the newest compatible version.
54
+ """
55
+ major, minor = _floor_parts(floor)
56
+ tags: list[str] = []
57
+ if major == 2:
58
+ for m in range(minor, _MIN_MANYLINUX_MINOR - 1, -1):
59
+ tags.append(f"manylinux_{major}_{m}_{arch}")
60
+ for m, alias in sorted(_LEGACY_ALIASES.items()):
61
+ if m <= minor:
62
+ tags.append(f"{alias}_{arch}")
63
+ else: # pragma: no cover - no manylinux_3_x exists yet
64
+ tags.append(f"manylinux_{major}_{minor}_{arch}")
65
+ return tuple(tags)
66
+
67
+
68
+ def linux_wheel_arch(tag: str) -> str | None:
69
+ """The architecture a single Linux wheel platform (sub)tag targets.
70
+
71
+ ``manylinux2014_x86_64`` / ``manylinux_2_17_x86_64`` / ``linux_x86_64`` ->
72
+ ``x86_64``. ``musllinux_*`` never covers (returns ``None``). A tag that is
73
+ not a linux tag at all also returns ``None``.
74
+ """
75
+ if tag.startswith("musllinux"):
76
+ return None
77
+ match = _LINUX_TAG_RE.match(tag)
78
+ return match.group("arch") if match else None
79
+
80
+
81
+ def is_plain_linux_tag(sub_tag: str) -> bool:
82
+ """A bare ``linux_<arch>`` platform tag (no manylinux/musllinux prefix).
83
+
84
+ Plain ``linux_*`` wheels carry **no glibc promise** — the tag says nothing
85
+ about which glibc the wheel was built against — so a distro-built one tied
86
+ to a newer glibc would abort the AppImage on older hosts.
87
+ """
88
+ return sub_tag.startswith("linux_")
89
+
90
+
91
+ def plain_linux_wheel_warning(wheel_name: str, sub_tag: str) -> str:
92
+ """The mandated warning for accepting a vendored plain ``linux_*`` wheel."""
93
+ return (
94
+ f"warning: accepting vendored wheel {wheel_name} on its {sub_tag} tag; "
95
+ f"plain linux_* wheels make no glibc promise, so the AppImage may fail "
96
+ f"on hosts with an older glibc than this wheel's build host. Portability "
97
+ f"is your call (prefer a manylinux-tagged wheel where available)."
98
+ )
99
+
100
+
101
+ def linux_wheel_coverage(wheel, archs: tuple[str, ...]) -> tuple[set[str], str | None]:
102
+ """Archs a locked *wheel* covers on Linux, plus an optional warning.
103
+
104
+ A manylinux/legacy tag covers its arch unconditionally. A plain
105
+ ``linux_<arch>`` tag covers **only when the wheel is vendored via
106
+ find_links** (``wheel.path`` is set) — a plain wheel pulled from
107
+ PyPI/an extra index makes no glibc promise and does *not* count toward
108
+ coverage — and yields the mandated warning when it does.
109
+ """
110
+ from_find_links = wheel.path is not None
111
+ covered: set[str] = set()
112
+ warning: str | None = None
113
+ for sub in wheel.platform_tag.split("."):
114
+ arch = linux_wheel_arch(sub)
115
+ if arch not in archs:
116
+ continue
117
+ if is_plain_linux_tag(sub):
118
+ if from_find_links:
119
+ covered.add(arch)
120
+ warning = plain_linux_wheel_warning(wheel.name, sub)
121
+ else:
122
+ covered.add(arch)
123
+ return covered, warning
124
+
125
+
126
+ def _subtag_glibc_level(tag: str) -> tuple[int, int] | None:
127
+ """The (major, minor) glibc a single manylinux sub-tag requires, or None."""
128
+ if tag.startswith("musllinux"):
129
+ return None
130
+ match = _PERENNIAL_RE.match(tag)
131
+ if match:
132
+ return int(match.group(1)), int(match.group(2))
133
+ if tag.startswith("manylinux1_"):
134
+ return 2, 5
135
+ if tag.startswith("manylinux2010_"):
136
+ return 2, 12
137
+ if tag.startswith("manylinux2014_"):
138
+ return 2, 17
139
+ return None # plain linux_* makes no glibc promise
140
+
141
+
142
+ def wheel_glibc_level(platform_tag: str) -> tuple[int, int] | None:
143
+ """The glibc a wheel requires = the *lowest* level among its sub-tags.
144
+
145
+ A compound (dot-joined) tag declares the wheel valid under any of its
146
+ platforms, so its real requirement is the least demanding one.
147
+ """
148
+ levels = [
149
+ lvl
150
+ for sub in platform_tag.split(".")
151
+ if (lvl := _subtag_glibc_level(sub)) is not None
152
+ ]
153
+ return min(levels) if levels else None
154
+
155
+
156
+ def effective_glibc_floor(lock: WheelRuntimeLock) -> str:
157
+ """The artifact's true host glibc requirement (derived, linux-spec).
158
+
159
+ ``max(runtime floor, highest locked wheel manylinux level)``. Computed on
160
+ demand from the locked wheels for ``doctor``/docs rather than stored as a
161
+ distinct lock field, keeping the shared lock model platform-agnostic.
162
+ """
163
+ floor = _floor_parts(lock.python_runtime.floor or DEFAULT_GLIBC_FLOOR)
164
+ for pkg in lock.packages:
165
+ for wheel in pkg.wheels:
166
+ level = wheel_glibc_level(wheel.platform_tag)
167
+ if level is not None and level > floor:
168
+ floor = level
169
+ return f"{floor[0]}.{floor[1]}"
170
+
171
+
172
+ class LinuxProfile(PlatformLockProfile):
173
+ platform = "linux"
174
+
175
+ def overlay(self, config: Config):
176
+ return config.linux
177
+
178
+ def missing_overlay_error(self) -> str:
179
+ return "pyproject.toml has no [tool.kivy.linux] table; nothing to lock."
180
+
181
+ def python_version(self, config: Config) -> str:
182
+ # [tool.kivy.linux.python].version is required (the loader raises a
183
+ # ConfigError when it is missing), so it is always set here. Never
184
+ # silently substitute a hidden default — that would pin an unexpected
185
+ # (and possibly unreleased) Python instead of surfacing the misconfig.
186
+ version = config.linux_required.python_version
187
+ if not version:
188
+ raise ConfigError(
189
+ "missing required [tool.kivy.linux.python].version",
190
+ key_path="tool.kivy.linux.python.version",
191
+ )
192
+ return version
193
+
194
+ def _floor(self, config: Config) -> str:
195
+ return config.linux_required.glibc_floor or DEFAULT_GLIBC_FLOOR
196
+
197
+ def variants(self, config: Config) -> tuple[Variant, ...]:
198
+ floor = self._floor(config)
199
+ variants: list[Variant] = []
200
+ for arch in config.linux_required.archs:
201
+ tags = manylinux_platform_tags(floor, arch)
202
+ variants.append(
203
+ Variant(
204
+ arch=arch,
205
+ platform_tag=tags[0],
206
+ extra_platform_tags=tags[1:],
207
+ )
208
+ )
209
+ return tuple(variants)
210
+
211
+ def wheel_covers(self, platform_tag: str, archs: tuple[str, ...]) -> set[str]:
212
+ covered: set[str] = set()
213
+ for sub in platform_tag.split("."):
214
+ arch = linux_wheel_arch(sub)
215
+ if arch in archs:
216
+ covered.add(arch)
217
+ return covered
218
+
219
+ def wheel_coverage(
220
+ self, wheel, archs: tuple[str, ...]
221
+ ) -> tuple[set[str], str | None]:
222
+ # Source-gate plain linux_* wheels (accepted only from find_links) and
223
+ # emit the mandated no-glibc-promise warning (linux-spec coverage rule).
224
+ return linux_wheel_coverage(wheel, archs)
225
+
226
+ def runtime_provider(self, config: Config) -> RuntimeProvider:
227
+ return PythonBuildStandaloneProvider()
228
+
229
+ def declared_floor(self, config: Config) -> str | None:
230
+ return config.linux_required.glibc_floor
231
+
232
+ def coverage_error(self, name: str, missing: list[str]) -> str:
233
+ return (
234
+ f"{name} has no manylinux wheel for arch(es): {', '.join(missing)} at "
235
+ f"the configured glibc floor.\n"
236
+ f" A compiled package must publish a manylinux wheel that runs on the "
237
+ f"floor to be locked reproducibly.\n"
238
+ f" Raise [tool.kivy.linux].glibc_floor to admit a newer-manylinux "
239
+ f"wheel, or supply the wheel via extra_index_urls/find_links."
240
+ )
241
+
242
+ def floor_error(self, declared: str, runtime_floor: str, version: str) -> str:
243
+ return (
244
+ f"glibc_floor {declared} is below the glibc {runtime_floor} floor "
245
+ f"required by the python-build-standalone {version} runtime.\n"
246
+ f" Raise [tool.kivy.linux].glibc_floor to at least {runtime_floor}."
247
+ )
248
+
249
+ def wheel_scope_hint(self) -> str:
250
+ return (
251
+ "Vendored wheels must live under the project directory, a sibling "
252
+ "directory, or the enclosing repository (e.g. a wheels/ directory "
253
+ "beside pyproject.toml)."
254
+ )
@@ -0,0 +1,75 @@
1
+ """Linux runtime provider — python-build-standalone with linux-gnu triples.
2
+
3
+ A one-line specialization of the generic :class:`PbsProvider`: it fixes the
4
+ arch→target-triple map to Linux's baseline ``*-unknown-linux-gnu`` builds (not
5
+ the ``_v2``/``_v3`` microarchitecture variants), and otherwise inherits the
6
+ shared PBS resolution + injectable metadata fetcher. The linux-gnu
7
+ ``install_only`` archives share the same relocatable unix-prefix layout as the
8
+ darwin ones, so no patchelf pass is needed (linux-spec).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from kivyforge.lock.wheelruntime.runtime import (
14
+ MetadataFetcher,
15
+ PbsProvider,
16
+ ReleaseAsset,
17
+ RuntimeProvider,
18
+ RuntimeProviderError,
19
+ pbs_asset_glob,
20
+ )
21
+
22
+ __all__ = [
23
+ "LINUX_TRIPLES",
24
+ "PythonBuildStandaloneProvider",
25
+ "ReleaseAsset",
26
+ "RuntimeProvider",
27
+ "RuntimeProviderError",
28
+ "get_runtime_provider",
29
+ "pbs_asset_name",
30
+ ]
31
+
32
+ # The manylinux2014-class glibc floor PBS's linux-gnu builds inherit from their
33
+ # build host. Recorded on the runtime so the lock/doctor can state (and the
34
+ # builder can validate against) the artifact's host requirement.
35
+ #
36
+ # IMPORTANT — this is a *pinned assumption* about PBS's build baseline, not a
37
+ # value derived from inspecting the resolved artifact. PBS's ``install_only``
38
+ # archives carry no structured glibc metadata, and `kivyforge lock` records only
39
+ # the artifact URL + SHA-256 (it does not download the runtime), so there is
40
+ # nothing to derive from at lock time. This constant is versioned with kivyforge:
41
+ # if PBS ever raises its linux-gnu baseline above 2.17, THIS VALUE MUST BE
42
+ # BUMPED IN LOCKSTEP, otherwise the lock/doctor would advertise a floor the
43
+ # binary no longer meets. (A deeper, deferred safeguard would ELF-inspect the
44
+ # staged ``bin/python3`` version-needs at build time to prove the floor.)
45
+ DEFAULT_GLIBC_FLOOR = "2.17"
46
+
47
+ # kivyforge arch name -> PBS/LLVM linux target triple (baseline microarch).
48
+ LINUX_TRIPLES = {
49
+ "x86_64": "x86_64-unknown-linux-gnu",
50
+ }
51
+
52
+ # Linux-facing alias for the generic PBS asset glob helper.
53
+ pbs_asset_name = pbs_asset_glob
54
+
55
+
56
+ class PythonBuildStandaloneProvider(PbsProvider):
57
+ """PBS provider fixed to Linux gnu triples (glibc 2.17 floor by default)."""
58
+
59
+ def __init__(
60
+ self,
61
+ metadata_fetcher: MetadataFetcher | None = None,
62
+ *,
63
+ floor: str | None = DEFAULT_GLIBC_FLOOR,
64
+ ) -> None:
65
+ super().__init__(LINUX_TRIPLES, metadata_fetcher, floor=floor)
66
+
67
+
68
+ def get_runtime_provider(
69
+ name: str = "python-build-standalone", **kwargs
70
+ ) -> RuntimeProvider:
71
+ if name in ("python-build-standalone", "pbs", "python_build_standalone"):
72
+ return PythonBuildStandaloneProvider(**kwargs)
73
+ raise RuntimeProviderError(
74
+ f"unknown runtime provider {name!r} (expected python-build-standalone)"
75
+ )
@@ -0,0 +1,47 @@
1
+ """Stage locked native binaries into the AppDir's ``usr/bin`` (linux-spec).
2
+
3
+ The ``[tool.kivy.linux.native.binaries]`` channel: each entry is fetched +
4
+ SHA-256-verified from ``pylock.linux.toml`` (URL or vendored path), then staged
5
+ into ``usr/bin`` — a single native file copied in with its exec bit set, or a
6
+ ``.zip`` / ``.tar.gz`` / ``.tgz`` of several extracted with a path-traversal
7
+ guard. The directory is created only when the lock actually pins binaries (no
8
+ empty ``bin/``). AppRun exposes ``usr/bin`` on ``PATH`` (helpers by name) and
9
+ appends it to ``LD_LIBRARY_PATH`` (libraries by soname).
10
+
11
+ The staging mechanics live in the shared
12
+ :mod:`kivyforge.artifacts.native_stage_util`; this module is the Linux-facing
13
+ wrapper that translates the neutral :class:`NativeStageError` into
14
+ ``AppDirError``.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+
21
+ from kivyforge.artifacts.cache import ArtifactCache
22
+ from kivyforge.artifacts.native_stage_util import NativeStageError, stage_binaries
23
+
24
+ from . import AppDirError
25
+ from .lock import LinuxLockfile
26
+
27
+
28
+ def stage_native_binaries(
29
+ lock: LinuxLockfile,
30
+ usr_dir: Path,
31
+ *,
32
+ project_root: Path,
33
+ cache: ArtifactCache | None = None,
34
+ no_cache: bool = False,
35
+ ) -> None:
36
+ """Stage every pinned native binary for the lock into ``usr_dir/bin``."""
37
+ try:
38
+ stage_binaries(
39
+ lock.native_binaries,
40
+ usr_dir,
41
+ project_root=project_root,
42
+ cache=cache,
43
+ no_cache=no_cache,
44
+ bin_label="usr/bin",
45
+ )
46
+ except NativeStageError as exc:
47
+ raise AppDirError(str(exc)) from exc