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,39 @@
1
+ """Generate the app-level ``PrivacyInfo.xcprivacy`` (spec 06).
2
+
3
+ Either copies the user's ``[tool.kivy.ios.privacy_manifest].source`` or writes a
4
+ minimal valid stub. This is the *app-level* manifest only; per-xcframework
5
+ privacy manifests are the framework author's responsibility (spec 06).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import plistlib
11
+ import shutil
12
+ from pathlib import Path
13
+
14
+ from kivyforge.config.model import Config
15
+
16
+ # Minimal valid privacy manifest: declares no tracking and no collected data.
17
+ STUB_MANIFEST: dict = {
18
+ "NSPrivacyTracking": False,
19
+ "NSPrivacyTrackingDomains": [],
20
+ "NSPrivacyCollectedDataTypes": [],
21
+ "NSPrivacyAccessedAPITypes": [],
22
+ }
23
+
24
+
25
+ def write_privacy_manifest(
26
+ config: Config, dest: str | Path, *, project_root: Path | None = None
27
+ ) -> Path:
28
+ dest = Path(dest)
29
+ source = config.ios.privacy_manifest_source if config.ios else None
30
+ if source:
31
+ root = project_root or Path.cwd()
32
+ src_path = (root / source).resolve()
33
+ if not src_path.is_file():
34
+ raise FileNotFoundError(f"privacy_manifest source not found: {source}")
35
+ shutil.copyfile(src_path, dest)
36
+ return dest
37
+ with open(dest, "wb") as f:
38
+ plistlib.dump(STUB_MANIFEST, f, sort_keys=True)
39
+ return dest
@@ -0,0 +1,165 @@
1
+ """Minimal valid ``project.pbxproj`` skeleton for bootstrapping (spec 06).
2
+
3
+ The ``pbxproj`` library mutates an existing project; it has no from-scratch
4
+ constructor. ``kivyforge build`` therefore parses this internal skeleton once
5
+ (substituting the app name + bundle id) and drives all subsequent wiring —
6
+ phases, files, build settings — through the library's API. This is *not*
7
+ user-facing cookiecutter templating: the skeleton is an implementation detail
8
+ and every meaningful object is added/synced programmatically afterwards.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ # Stable 24-hex object IDs for the skeleton. pbxproj generates fresh IDs for
14
+ # everything added later; these only need to be internally consistent.
15
+ _SKELETON = """// !$*UTF8*$!
16
+ {
17
+ \tarchiveVersion = 1;
18
+ \tclasses = {
19
+ \t};
20
+ \tobjectVersion = 56;
21
+ \tobjects = {
22
+
23
+ \t\t1A0000000000000000000001 /* Sources */ = {
24
+ \t\t\tisa = PBXSourcesBuildPhase;
25
+ \t\t\tbuildActionMask = 2147483647;
26
+ \t\t\tfiles = (
27
+ \t\t\t);
28
+ \t\t\trunOnlyForDeploymentPostprocessing = 0;
29
+ \t\t};
30
+ \t\t1A0000000000000000000002 /* Frameworks */ = {
31
+ \t\t\tisa = PBXFrameworksBuildPhase;
32
+ \t\t\tbuildActionMask = 2147483647;
33
+ \t\t\tfiles = (
34
+ \t\t\t);
35
+ \t\t\trunOnlyForDeploymentPostprocessing = 0;
36
+ \t\t};
37
+ \t\t1A0000000000000000000003 /* Resources */ = {
38
+ \t\t\tisa = PBXResourcesBuildPhase;
39
+ \t\t\tbuildActionMask = 2147483647;
40
+ \t\t\tfiles = (
41
+ \t\t\t);
42
+ \t\t\trunOnlyForDeploymentPostprocessing = 0;
43
+ \t\t};
44
+
45
+ \t\t1A0000000000000000000010 /* {APP_NAME}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "{APP_NAME}.app"; sourceTree = BUILT_PRODUCTS_DIR; };
46
+
47
+ \t\t1A0000000000000000000020 = {
48
+ \t\t\tisa = PBXGroup;
49
+ \t\t\tchildren = (
50
+ \t\t\t\t1A0000000000000000000021 /* Products */,
51
+ \t\t\t);
52
+ \t\t\tsourceTree = "<group>";
53
+ \t\t};
54
+ \t\t1A0000000000000000000021 /* Products */ = {
55
+ \t\t\tisa = PBXGroup;
56
+ \t\t\tchildren = (
57
+ \t\t\t\t1A0000000000000000000010 /* {APP_NAME}.app */,
58
+ \t\t\t);
59
+ \t\t\tname = Products;
60
+ \t\t\tsourceTree = "<group>";
61
+ \t\t};
62
+
63
+ \t\t1A0000000000000000000030 /* {APP_NAME} */ = {
64
+ \t\t\tisa = PBXNativeTarget;
65
+ \t\t\tbuildConfigurationList = 1A0000000000000000000040 /* Build configuration list for PBXNativeTarget */;
66
+ \t\t\tbuildPhases = (
67
+ \t\t\t\t1A0000000000000000000001 /* Sources */,
68
+ \t\t\t\t1A0000000000000000000003 /* Resources */,
69
+ \t\t\t\t1A0000000000000000000002 /* Frameworks */,
70
+ \t\t\t);
71
+ \t\t\tbuildRules = (
72
+ \t\t\t);
73
+ \t\t\tdependencies = (
74
+ \t\t\t);
75
+ \t\t\tname = "{APP_NAME}";
76
+ \t\t\tproductName = "{APP_NAME}";
77
+ \t\t\tproductReference = 1A0000000000000000000010 /* {APP_NAME}.app */;
78
+ \t\t\tproductType = "com.apple.product-type.application";
79
+ \t\t};
80
+
81
+ \t\t1A0000000000000000000050 /* Project object */ = {
82
+ \t\t\tisa = PBXProject;
83
+ \t\t\tattributes = {
84
+ \t\t\t\tLastUpgradeCheck = 2600;
85
+ \t\t\t\tTargetAttributes = {
86
+ \t\t\t\t};
87
+ \t\t\t};
88
+ \t\t\tbuildConfigurationList = 1A0000000000000000000060 /* Build configuration list for PBXProject */;
89
+ \t\t\tcompatibilityVersion = "Xcode 14.0";
90
+ \t\t\tdevelopmentRegion = en;
91
+ \t\t\thasScannedForEncodings = 0;
92
+ \t\t\tknownRegions = (
93
+ \t\t\t\ten,
94
+ \t\t\t\tBase,
95
+ \t\t\t);
96
+ \t\t\tmainGroup = 1A0000000000000000000020;
97
+ \t\t\tproductRefGroup = 1A0000000000000000000021 /* Products */;
98
+ \t\t\tprojectDirPath = "";
99
+ \t\t\tprojectRoot = "";
100
+ \t\t\ttargets = (
101
+ \t\t\t\t1A0000000000000000000030 /* {APP_NAME} */,
102
+ \t\t\t);
103
+ \t\t};
104
+
105
+ \t\t1A0000000000000000000041 /* Debug */ = {
106
+ \t\t\tisa = XCBuildConfiguration;
107
+ \t\t\tbuildSettings = {
108
+ \t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = "{BUNDLE_ID}";
109
+ \t\t\t\tPRODUCT_NAME = "$(TARGET_NAME)";
110
+ \t\t\t};
111
+ \t\t\tname = Debug;
112
+ \t\t};
113
+ \t\t1A0000000000000000000042 /* Release */ = {
114
+ \t\t\tisa = XCBuildConfiguration;
115
+ \t\t\tbuildSettings = {
116
+ \t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = "{BUNDLE_ID}";
117
+ \t\t\t\tPRODUCT_NAME = "$(TARGET_NAME)";
118
+ \t\t\t};
119
+ \t\t\tname = Release;
120
+ \t\t};
121
+ \t\t1A0000000000000000000040 /* Build configuration list for PBXNativeTarget */ = {
122
+ \t\t\tisa = XCConfigurationList;
123
+ \t\t\tbuildConfigurations = (
124
+ \t\t\t\t1A0000000000000000000041 /* Debug */,
125
+ \t\t\t\t1A0000000000000000000042 /* Release */,
126
+ \t\t\t);
127
+ \t\t\tdefaultConfigurationIsVisible = 0;
128
+ \t\t\tdefaultConfigurationName = Release;
129
+ \t\t};
130
+
131
+ \t\t1A0000000000000000000061 /* Debug */ = {
132
+ \t\t\tisa = XCBuildConfiguration;
133
+ \t\t\tbuildSettings = {
134
+ \t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;
135
+ \t\t\t\tSDKROOT = iphoneos;
136
+ \t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;
137
+ \t\t\t};
138
+ \t\t\tname = Debug;
139
+ \t\t};
140
+ \t\t1A0000000000000000000062 /* Release */ = {
141
+ \t\t\tisa = XCBuildConfiguration;
142
+ \t\t\tbuildSettings = {
143
+ \t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;
144
+ \t\t\t\tSDKROOT = iphoneos;
145
+ \t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;
146
+ \t\t\t};
147
+ \t\t\tname = Release;
148
+ \t\t};
149
+ \t\t1A0000000000000000000060 /* Build configuration list for PBXProject */ = {
150
+ \t\t\tisa = XCConfigurationList;
151
+ \t\t\tbuildConfigurations = (
152
+ \t\t\t\t1A0000000000000000000061 /* Debug */,
153
+ \t\t\t\t1A0000000000000000000062 /* Release */,
154
+ \t\t\t);
155
+ \t\t\tdefaultConfigurationIsVisible = 0;
156
+ \t\t\tdefaultConfigurationName = Release;
157
+ \t\t};
158
+ \t};
159
+ \trootObject = 1A0000000000000000000050 /* Project object */;
160
+ }
161
+ """
162
+
163
+
164
+ def skeleton_pbxproj(app_name: str, bundle_id: str) -> str:
165
+ return _SKELETON.replace("{APP_NAME}", app_name).replace("{BUNDLE_ID}", bundle_id)
@@ -0,0 +1,63 @@
1
+ """Render the fixed source files and the per-project ``main_config.h`` (spec 06).
2
+
3
+ Generated source layout in <app>-ios/:
4
+ main.m — trivial entry point; calls kivyforge_main()
5
+ kivyforge_bootstrap.h — bootstrap public header
6
+ kivyforge_bootstrap.m — dual-mode bootstrap (SDL3 for Kivy, UIKit for pure-Python)
7
+ main_config.h — per-project defines (entry point, py version)
8
+
9
+ Mobile window/display geometry (DPI, scale, safe area, keyboard height) is no
10
+ longer vendored here — it ships in Kivy core as ``kivy.mobile`` (kivy/kivy#9331).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from pathlib import Path
16
+
17
+ from kivyforge.config.model import Config
18
+
19
+ _TEMPLATES = Path(__file__).parent / "templates"
20
+
21
+ # Files that are copied verbatim from templates/ into the generated project.
22
+ _BOOTSTRAP_FILES = ("kivyforge_bootstrap.h", "kivyforge_bootstrap.m")
23
+
24
+
25
+ def main_m_source() -> str:
26
+ return (_TEMPLATES / "main.m").read_text(encoding="utf-8")
27
+
28
+
29
+ def render_main_config_h(config: Config, *, python_version: str | None = None) -> str:
30
+ """Fill the main_config.h template with APP_DIR / ENTRY_POINT / py version.
31
+
32
+ ``app/`` is the symlink name inside ``<app>-ios/`` (always ``app``), so
33
+ APP_DIR is the bundle-relative folder name, not the user's ``app_dir`` path.
34
+ """
35
+ version = (
36
+ python_version
37
+ or (config.ios.python_version if config.ios else None)
38
+ or "3.15.0"
39
+ )
40
+ major_minor = ".".join(version.split(".")[:2])
41
+ template = (_TEMPLATES / "main_config.h.in").read_text(encoding="utf-8")
42
+ return (
43
+ template.replace("@APP_DIR@", "app")
44
+ .replace("@ENTRY_POINT@", config.kivy.entry_point)
45
+ .replace("@PYTHON_MAJOR_MINOR@", major_minor)
46
+ )
47
+
48
+
49
+ def write_sources(
50
+ config: Config, project_dir: str | Path, *, python_version: str | None = None
51
+ ) -> tuple[Path, Path]:
52
+ project_dir = Path(project_dir)
53
+ main_m = project_dir / "main.m"
54
+ main_h = project_dir / "main_config.h"
55
+ main_m.write_text(main_m_source(), encoding="utf-8")
56
+ main_h.write_text(
57
+ render_main_config_h(config, python_version=python_version), encoding="utf-8"
58
+ )
59
+ for fname in _BOOTSTRAP_FILES:
60
+ (project_dir / fname).write_text(
61
+ (_TEMPLATES / fname).read_text(encoding="utf-8"), encoding="utf-8"
62
+ )
63
+ return main_m, main_h
@@ -0,0 +1,225 @@
1
+ """Create and maintain the ``<app>-ios/`` staging tree (spec 06)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ from collections.abc import Callable
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+
11
+ from kivyforge.bundle.pycompile import PycompileError, byte_compile, select_compiler
12
+ from kivyforge.config.model import Config
13
+
14
+ _APP_COPY_IGNORE = shutil.ignore_patterns("__pycache__", "*.pyc")
15
+
16
+
17
+ class StagingError(Exception):
18
+ """Staging tree cannot be created (e.g. ``app_dir`` does not exist)."""
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class StagingLayout:
23
+ root: Path # <app>-ios/
24
+
25
+ @property
26
+ def xcodeproj(self) -> Path:
27
+ return self.root / f"{self.root.name[:-4]}.xcodeproj"
28
+
29
+ @property
30
+ def python_xcframework(self) -> Path:
31
+ return self.root / "Python.xcframework"
32
+
33
+ @property
34
+ def frameworks(self) -> Path:
35
+ return self.root / "Frameworks"
36
+
37
+ @property
38
+ def app(self) -> Path:
39
+ return self.root / "app"
40
+
41
+ @property
42
+ def pip_deps_simulator(self) -> Path:
43
+ return self.root / "pip-deps-simulator"
44
+
45
+ @property
46
+ def pip_deps_device(self) -> Path:
47
+ return self.root / "pip-deps-device"
48
+
49
+ def pip_deps_for_slice(self, target: str) -> Path:
50
+ """Return the slice-specific pip-deps directory for *target*."""
51
+ return (
52
+ self.pip_deps_simulator if target == "simulator" else self.pip_deps_device
53
+ )
54
+
55
+ @property
56
+ def resources(self) -> Path:
57
+ return self.root / "Resources"
58
+
59
+
60
+ def staging_dir_name(config: Config) -> str:
61
+ return f"{config.app_slug}-ios"
62
+
63
+
64
+ def create_staging(
65
+ config: Config,
66
+ project_root: str | Path,
67
+ *,
68
+ release: bool = False,
69
+ python_version: str | None = None,
70
+ echo: Callable[[str], None] = lambda msg: None,
71
+ ) -> StagingLayout:
72
+ """Create the ``<app>-ios/`` tree and the ``app/`` source.
73
+
74
+ Dev builds (``release=False``, the default) get a symlink to ``app_dir``
75
+ (spec 06 "symlink, not copy") for fast edit-rebuild iteration — the
76
+ symlink is created once and refreshed only if it points somewhere other
77
+ than the current ``app_dir``. Release builds (``kivyforge package``) get a
78
+ real, disposable copy instead, so ``[tool.kivy.ios.python.build_settings]``
79
+ byte-compilation/stripping can run against it without ever touching the
80
+ user's actual source tree — *release* requires *python_version* (the
81
+ pinned ``Python.xcframework`` version) for that.
82
+ """
83
+ if release and python_version is None:
84
+ raise ValueError("create_staging(release=True) requires python_version")
85
+
86
+ project_root = Path(project_root)
87
+ root = project_root / staging_dir_name(config)
88
+ layout = StagingLayout(root=root)
89
+
90
+ for d in (
91
+ root,
92
+ layout.frameworks,
93
+ layout.pip_deps_device,
94
+ layout.pip_deps_simulator,
95
+ layout.resources,
96
+ ):
97
+ d.mkdir(parents=True, exist_ok=True)
98
+
99
+ if release:
100
+ _materialize_app_copy(layout, project_root, config, python_version, echo)
101
+ else:
102
+ _refresh_app_symlink(layout, project_root, config.kivy.app_dir)
103
+ return layout
104
+
105
+
106
+ def _refresh_app_symlink(
107
+ layout: StagingLayout, project_root: Path, app_dir: str
108
+ ) -> None:
109
+ source = project_root / app_dir
110
+ if not source.is_dir():
111
+ raise StagingError(
112
+ f"app_dir '{app_dir}' is not an existing directory "
113
+ f"(looked for {source}). Create it or fix [tool.kivy].app_dir in "
114
+ f"pyproject.toml — it must point at your app's Python source."
115
+ )
116
+ # The symlink target is relative to <app>-ios/ (e.g. ../src).
117
+ target = os.path.relpath(source.resolve(), layout.root)
118
+ link = layout.app
119
+ if link.is_symlink():
120
+ if os.readlink(link) == target:
121
+ return
122
+ link.unlink()
123
+ elif link.is_dir():
124
+ # A real directory here is a previous release build's materialized
125
+ # copy (create_staging(release=True)), not foreign user data — always
126
+ # kivyforge-owned output at this exact path. Reclaim it for the
127
+ # symlink so switching back to build/run after a package works.
128
+ shutil.rmtree(link)
129
+ elif link.exists():
130
+ raise FileExistsError(
131
+ f"{link} exists and is not a symlink; remove it and re-run build."
132
+ )
133
+ link.symlink_to(target)
134
+
135
+
136
+ def _materialize_app_copy(
137
+ layout: StagingLayout,
138
+ project_root: Path,
139
+ config: Config,
140
+ python_version: str,
141
+ echo: Callable[[str], None],
142
+ ) -> None:
143
+ source = project_root / config.kivy.app_dir
144
+ if not source.is_dir():
145
+ raise StagingError(
146
+ f"app_dir '{config.kivy.app_dir}' is not an existing directory "
147
+ f"(looked for {source}). Create it or fix [tool.kivy].app_dir in "
148
+ f"pyproject.toml — it must point at your app's Python source."
149
+ )
150
+ link = layout.app
151
+ # Always start clean: byte-compiling mutates the copy destructively
152
+ # (deletes .py once a .pyc exists), so a stale copy must never be reused
153
+ # across builds, and a leftover symlink from an earlier dev build must
154
+ # never be mistaken for the release copy.
155
+ if link.is_symlink():
156
+ link.unlink()
157
+ elif link.is_dir():
158
+ shutil.rmtree(link)
159
+ elif link.exists():
160
+ raise FileExistsError(
161
+ f"{link} exists and is neither a symlink nor a directory; remove "
162
+ "it and re-run build."
163
+ )
164
+ shutil.copytree(source, link, ignore=_APP_COPY_IGNORE)
165
+ _compile_app_copy(link, config, python_version, echo)
166
+
167
+
168
+ def _setting_applies(value: bool | str, *, release: bool) -> bool:
169
+ """A ``build_settings`` tri-state resolved for the build being produced."""
170
+ if isinstance(value, bool):
171
+ return value
172
+ return release # DESKTOP_RELEASE_ONLY
173
+
174
+
175
+ def _compile_app_copy(
176
+ app: Path,
177
+ config: Config,
178
+ python_version: str,
179
+ echo: Callable[[str], None],
180
+ ) -> None:
181
+ """Byte-compile/strip the materialized app copy, per ``build_settings``.
182
+
183
+ Only ever called for the release copy (``release=True`` is implicit here
184
+ — this function is unreachable from the dev symlink path), so the
185
+ tri-state resolves against ``release=True`` unconditionally. Mirrors
186
+ ``artifacts/collect.py::_compile_pip_deps``: the Python.xcframework ships
187
+ no standalone interpreter to shell out to, so the compiler ladder always
188
+ falls to "this process's interpreter, if its CPython minor matches", or
189
+ degrades.
190
+ """
191
+ settings = config.ios_required.python_build_settings
192
+ if not _setting_applies(settings.byte_compile, release=True):
193
+ return
194
+ compiler = select_compiler(
195
+ staged_interpreter=Path(), native=False, python_version=python_version
196
+ )
197
+ if compiler is None:
198
+ message = (
199
+ f"this project ships CPython {python_version}, and no CPython of "
200
+ "that minor could be found to byte-compile with (a .pyc is only "
201
+ "loadable by the exact CPython minor that wrote it)"
202
+ )
203
+ if settings.byte_compile is True:
204
+ raise StagingError(
205
+ "[tool.kivy.ios.python.build_settings].byte_compile = true but "
206
+ f"{message}.\n"
207
+ " Install a matching CPython, or set byte_compile = false."
208
+ )
209
+ echo(f"[stage] not byte-compiling app sources: {message}.")
210
+ return
211
+ strip_source = _setting_applies(settings.strip_source, release=True)
212
+ with_what = " ".join(compiler) if compiler else "this interpreter"
213
+ echo(
214
+ f"[stage] byte-compiling app sources with {with_what}"
215
+ + (" (.pyc only)" if strip_source else "")
216
+ )
217
+ try:
218
+ byte_compile(
219
+ [app], compiler=compiler, strip_source=strip_source, stripdir=app.parent
220
+ )
221
+ except PycompileError as exc:
222
+ raise StagingError(
223
+ f"{exc}\n Fix it, or set "
224
+ "[tool.kivy.ios.python.build_settings].byte_compile = false."
225
+ ) from exc