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,318 @@
1
+ """Programmatic ``<app>.xcodeproj`` generation via ``pbxproj`` (spec 06).
2
+
3
+ Bootstraps from the internal skeleton on first run, then performs the spec's
4
+ idempotent "every build" sync: managed build settings (per-config), the Build
5
+ Python run script (ordered between Copy Bundle Resources and Frameworks),
6
+ ``main.m`` in Sources, Python.xcframework + Frameworks/* embed & sign, the app/
7
+ folder reference, and signing/user settings.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pathlib import Path
13
+ from typing import Any, cast
14
+
15
+ from pbxproj import XcodeProject
16
+ from pbxproj.pbxextensions.ProjectFiles import FileOptions, ProjectFiles, TreeType
17
+
18
+ from kivyforge.config.model import Config
19
+
20
+ from .buildsettings import (
21
+ BUILD_PYTHON_SCRIPT,
22
+ managed_settings,
23
+ signing_settings,
24
+ user_build_settings,
25
+ )
26
+ from .lock.model import LockedSwiftPackage, LockedXcframework
27
+ from .skeleton import skeleton_pbxproj
28
+ from .staging import StagingError, StagingLayout
29
+ from .swift_packages import sync_swift_packages, write_package_resolved
30
+ from .xcframeworks import sync_native_frameworks
31
+
32
+ CONFIGURATIONS = ("Debug", "Release")
33
+
34
+ # Xcode shows "Update to recommended settings" when the project's
35
+ # LastUpgradeCheck is below the running Xcode's version. `kivyforge build`
36
+ # detects the installed Xcode and passes its encoded version down (see
37
+ # kivyforge/cli/build.py); this constant is only the fallback used when Xcode
38
+ # can't be queried. Keep it at/above the minimum supported Xcode.
39
+ RECOMMENDED_LAST_UPGRADE_CHECK = "2650"
40
+
41
+ # pbxproj does not ship .storyboard; register for LaunchScreen.storyboard sync.
42
+ ProjectFiles._FILE_TYPES.setdefault(
43
+ ".storyboard", ("file.storyboard", "PBXResourcesBuildPhase")
44
+ )
45
+
46
+
47
+ class XcodeProjectGenerator:
48
+ def __init__(
49
+ self,
50
+ config: Config,
51
+ layout: StagingLayout,
52
+ *,
53
+ last_upgrade_check: str | None = None,
54
+ swift_packages: tuple[LockedSwiftPackage, ...] = (),
55
+ xcframeworks: tuple[LockedXcframework, ...] = (),
56
+ ) -> None:
57
+ self.config = config
58
+ self.layout = layout
59
+ self.app_name = config.app_slug
60
+ self.last_upgrade_check = last_upgrade_check or RECOMMENDED_LAST_UPGRADE_CHECK
61
+ self.swift_packages = swift_packages
62
+ self.xcframeworks = xcframeworks
63
+
64
+ @property
65
+ def pbxproj_path(self) -> Path:
66
+ return self.layout.xcodeproj / "project.pbxproj"
67
+
68
+ def generate(self) -> Path:
69
+ project = self._load_or_bootstrap()
70
+ self._sync_project_attributes(project)
71
+ self._apply_build_settings(project)
72
+ self._sync_run_script(project)
73
+ self._sync_sources(project)
74
+ self._sync_bundle_folders(project)
75
+ self._sync_pip_deps_browse_reference(project)
76
+ self._sync_resources(project)
77
+ self._sync_embedded_frameworks(project)
78
+ self._sync_swift_packages(project)
79
+ project.save()
80
+ self._write_package_resolved()
81
+ return self.pbxproj_path
82
+
83
+ # -- swift packages (spec 07) ------------------------------------------ #
84
+ def _sync_swift_packages(self, project: XcodeProject) -> None:
85
+ sync_swift_packages(
86
+ project,
87
+ self.app_name,
88
+ self.swift_packages,
89
+ staging_root=self.layout.root,
90
+ )
91
+
92
+ def _write_package_resolved(self) -> None:
93
+ resolved = (
94
+ self.layout.xcodeproj
95
+ / "project.xcworkspace"
96
+ / "xcshareddata"
97
+ / "swiftpm"
98
+ / "Package.resolved"
99
+ )
100
+ write_package_resolved(resolved, self.swift_packages)
101
+
102
+ # -- bootstrap ---------------------------------------------------------- #
103
+ def _load_or_bootstrap(self) -> XcodeProject:
104
+ if self.pbxproj_path.is_file():
105
+ return XcodeProject.load(str(self.pbxproj_path))
106
+ self.layout.xcodeproj.mkdir(parents=True, exist_ok=True)
107
+ assert self.config.ios is not None
108
+ self.pbxproj_path.write_text(
109
+ skeleton_pbxproj(self.app_name, self.config.ios.bundle_id),
110
+ encoding="utf-8",
111
+ )
112
+ return XcodeProject.load(str(self.pbxproj_path))
113
+
114
+ # -- project attributes ------------------------------------------------- #
115
+ def _sync_project_attributes(self, project: XcodeProject) -> None:
116
+ """Keep LastUpgradeCheck current so Xcode doesn't prompt to "Update to
117
+ recommended settings" (self-heals projects bootstrapped by older runs).
118
+ """
119
+ for proj in cast(Any, project).objects.get_objects_in_section("PBXProject"):
120
+ attributes = proj["attributes"]
121
+ attributes["LastUpgradeCheck"] = self.last_upgrade_check
122
+
123
+ # -- build settings ----------------------------------------------------- #
124
+ def _apply_build_settings(self, project: XcodeProject) -> None:
125
+ signing = signing_settings(self.config)
126
+ user = user_build_settings(self.config)
127
+ for configuration in CONFIGURATIONS:
128
+ project.set_flags(
129
+ "ALWAYS_SEARCH_USER_PATHS",
130
+ "NO",
131
+ configuration_name=configuration,
132
+ )
133
+ settings = managed_settings(
134
+ self.config, configuration=configuration, layout=self.layout
135
+ )
136
+ # user settings layered under managed (reserved keys already rejected)
137
+ merged = {**user, **settings, **signing}
138
+ for key, value in merged.items():
139
+ project.set_flags(
140
+ key,
141
+ value,
142
+ target_name=self.app_name,
143
+ configuration_name=configuration,
144
+ )
145
+
146
+ # -- run script --------------------------------------------------------- #
147
+ def _sync_run_script(self, project: XcodeProject) -> None:
148
+ # Idempotent: drop any prior copy, re-add, then order it between Copy
149
+ # Bundle Resources and the Frameworks (Link) phase.
150
+ project.remove_run_script(BUILD_PYTHON_SCRIPT)
151
+ project.add_run_script(BUILD_PYTHON_SCRIPT, target_name=self.app_name)
152
+ self._configure_run_script(project)
153
+ self._order_run_script(project)
154
+
155
+ def _configure_run_script(self, project: XcodeProject) -> None:
156
+ # Spec 06: run every build (install_python walks app/ + pip-deps/).
157
+ target = project.get_target_by_name(self.app_name)
158
+ if target is None:
159
+ raise RuntimeError(f"target {self.app_name!r} not found in Xcode project")
160
+ for phase_id in target.buildPhases:
161
+ phase = project.get_object(phase_id)
162
+ if (
163
+ getattr(phase, "isa", None) == "PBXShellScriptBuildPhase"
164
+ and getattr(phase, "shellScript", None) == BUILD_PYTHON_SCRIPT
165
+ ):
166
+ phase["alwaysOutOfDate"] = 1
167
+ break
168
+
169
+ def _order_run_script(self, project: XcodeProject) -> None:
170
+ target = project.get_target_by_name(self.app_name)
171
+ if target is None:
172
+ raise RuntimeError(f"target {self.app_name!r} not found in Xcode project")
173
+ phases = list(target.buildPhases)
174
+ kinds = {pid: project.get_object(pid).isa for pid in phases}
175
+ script = next(
176
+ (p for p in phases if kinds[p] == "PBXShellScriptBuildPhase"), None
177
+ )
178
+ frameworks = next(
179
+ (p for p in phases if kinds[p] == "PBXFrameworksBuildPhase"), None
180
+ )
181
+ if script is None or frameworks is None:
182
+ return
183
+ phases.remove(script)
184
+ idx = phases.index(frameworks)
185
+ phases.insert(idx, script)
186
+ target.buildPhases = phases
187
+
188
+ # -- sources ------------------------------------------------------------ #
189
+ def _sync_sources(self, project: XcodeProject) -> None:
190
+ sources_group = project.get_or_create_group("Sources")
191
+ for source_file in ("main.m", "kivyforge_bootstrap.m"):
192
+ fpath = self.layout.root / source_file
193
+ if fpath.is_file() and not project.get_files_by_name(source_file):
194
+ project.add_file(
195
+ source_file,
196
+ parent=sources_group,
197
+ target_name=self.app_name,
198
+ tree="SOURCE_ROOT",
199
+ file_options=FileOptions(embed_framework=False),
200
+ )
201
+
202
+ # -- bundle resources --------------------------------------------------- #
203
+ def _sync_bundle_folders(self, project: XcodeProject) -> None:
204
+ """Add ``app/`` as a folder reference (spec 06).
205
+
206
+ ``app/`` is mandatory — it is the symlink to the user's ``app_dir`` and
207
+ carries the app's Python source into the bundle. If it does not resolve
208
+ to an existing directory, fail loudly rather than silently shipping an
209
+ app with no code.
210
+
211
+ ``pip-deps/`` is intentionally *not* a Copy Bundle Resources folder
212
+ reference. It is platform-sliced (``pip-deps-simulator`` /
213
+ ``pip-deps-device``) and the Build Python run script rsyncs the correct
214
+ slice into the bundle on every build; a folder reference here would
215
+ stage one fixed slice — the wrong one for half of all builds — ahead of
216
+ that rsync.
217
+ """
218
+ if not self.layout.app.exists():
219
+ raise StagingError(
220
+ f"app source directory is missing: {self.layout.app} does not "
221
+ f"resolve to an existing directory (app_dir = "
222
+ f"{self.config.kivy.app_dir!r}). Fix [tool.kivy].app_dir in "
223
+ f"pyproject.toml."
224
+ )
225
+ if not project.get_files_by_name("app"):
226
+ project.add_file(
227
+ "app",
228
+ target_name=self.app_name,
229
+ tree="SOURCE_ROOT",
230
+ file_options=FileOptions(embed_framework=False),
231
+ )
232
+ # pbxproj type detection uses CWD; force folder refs for bundle copy.
233
+ for ref in project.get_files_by_name("app"):
234
+ ref.set_last_known_file_type("folder")
235
+
236
+ def _sync_pip_deps_browse_reference(self, project: XcodeProject) -> None:
237
+ """Show the pip-deps packages in the navigator without building them.
238
+
239
+ pip-deps is staged into the bundle by the Build Python run script, not
240
+ Copy Bundle Resources, so it would otherwise be invisible in Xcode's
241
+ project navigator. Add the simulator slice as a folder reference for
242
+ browsing only: ``create_build_files=False`` keeps it out of every build
243
+ phase, so it has no effect on the build (the package set is identical
244
+ across slices). Its on-disk path is ``pip-deps-simulator``; only its
245
+ display name is ``pip-deps``.
246
+ """
247
+ if not self.layout.pip_deps_simulator.is_dir():
248
+ return
249
+ if project.get_files_by_path("pip-deps-simulator", tree=TreeType.SOURCE_ROOT):
250
+ return # already present (idempotent)
251
+ project.add_file(
252
+ "pip-deps-simulator",
253
+ tree=TreeType.SOURCE_ROOT,
254
+ force=True,
255
+ file_options=FileOptions(create_build_files=False, embed_framework=False),
256
+ )
257
+ for ref in project.get_files_by_path(
258
+ "pip-deps-simulator", tree=TreeType.SOURCE_ROOT
259
+ ):
260
+ ref.set_last_known_file_type("folder")
261
+ ref["name"] = "pip-deps"
262
+
263
+ def _sync_resources(self, project: XcodeProject) -> None:
264
+ opts = FileOptions(embed_framework=False)
265
+ # PrivacyInfo.xcprivacy must ship in the bundle (Apple required-reason
266
+ # APIs); pbxproj already maps .xcprivacy -> Copy Bundle Resources.
267
+ privacy = self.layout.root / "PrivacyInfo.xcprivacy"
268
+ if privacy.is_file() and not project.get_files_by_name("PrivacyInfo.xcprivacy"):
269
+ project.add_file(
270
+ "PrivacyInfo.xcprivacy",
271
+ target_name=self.app_name,
272
+ tree="SOURCE_ROOT",
273
+ file_options=opts,
274
+ )
275
+ catalog = self.layout.resources / "Assets.xcassets"
276
+ if catalog.is_dir() and not project.get_files_by_name("Assets.xcassets"):
277
+ project.add_file(
278
+ "Resources/Assets.xcassets",
279
+ target_name=self.app_name,
280
+ tree="SOURCE_ROOT",
281
+ file_options=opts,
282
+ )
283
+ launch = self.layout.root / "LaunchScreen.storyboard"
284
+ if launch.is_file() and not project.get_files_by_name(
285
+ "LaunchScreen.storyboard"
286
+ ):
287
+ project.add_file(
288
+ "LaunchScreen.storyboard",
289
+ target_name=self.app_name,
290
+ tree="SOURCE_ROOT",
291
+ file_options=opts,
292
+ )
293
+
294
+ # -- frameworks (embed & sign) ----------------------------------------- #
295
+ def _sync_embedded_frameworks(self, project: XcodeProject) -> None:
296
+ embed = FileOptions(embed_framework=True, code_sign_on_copy=True)
297
+ frameworks_group = project.get_or_create_group("Frameworks", "Frameworks")
298
+ # Python.xcframework: Embed & Sign (Link + Embed) per python.org docs.
299
+ if self.layout.python_xcframework.exists() and not project.get_files_by_name(
300
+ "Python.xcframework"
301
+ ):
302
+ project.add_file(
303
+ "Python.xcframework",
304
+ target_name=self.app_name,
305
+ tree="SOURCE_ROOT",
306
+ file_options=embed,
307
+ )
308
+ # Frameworks/*.xcframework — link/embed per the locked native entries
309
+ # (wheel-embedded SDL3/ANGLE default to Embed & Sign); stale refs for
310
+ # frameworks no longer on disk are pruned.
311
+ link_embed = {xc.name: (xc.link, xc.embed) for xc in self.xcframeworks}
312
+ sync_native_frameworks(
313
+ project,
314
+ self.app_name,
315
+ frameworks_dir=self.layout.frameworks,
316
+ frameworks_group=frameworks_group,
317
+ link_embed=link_embed,
318
+ )
@@ -0,0 +1,71 @@
1
+ """iOS lock stack (``pylock.ios.toml``, spec 02).
2
+
3
+ The iOS public lock surface: resolve dependencies + native artifacts into a
4
+ ``Lockfile``, serialize/parse ``pylock.ios.toml``, and compare locks. The
5
+ platform-neutral pieces (the ``[[packages]]`` model, PEP 751 helpers, the
6
+ drift check, and the neutral pip helpers) live in ``kivyforge.lock``.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from kivyforge.lock.model import LockedPackage, LockedWheel
12
+
13
+ from .builder import BuildError, build_lockfile, diff_summary, semantic_equal
14
+ from .model import (
15
+ LockedSwiftPackage,
16
+ LockedXcframework,
17
+ Lockfile,
18
+ PythonXcframework,
19
+ )
20
+ from .reader import LockError, compute_pyproject_sha256, is_in_sync, load, loads
21
+ from .resolver import (
22
+ PipResolver,
23
+ ResolvedPackage,
24
+ ResolvedWheel,
25
+ Resolver,
26
+ ResolverError,
27
+ get_resolver,
28
+ slice_tags,
29
+ )
30
+ from .spm import (
31
+ ResolvedSwiftPackage,
32
+ SpmResolver,
33
+ SpmResolverError,
34
+ XcodeSpmResolver,
35
+ get_spm_resolver,
36
+ )
37
+ from .writer import dumps
38
+ from .xcframework import XcframeworkResolverError, resolve_xcframeworks
39
+
40
+ __all__ = [
41
+ "BuildError",
42
+ "build_lockfile",
43
+ "diff_summary",
44
+ "semantic_equal",
45
+ "Lockfile",
46
+ "LockedPackage",
47
+ "LockedSwiftPackage",
48
+ "LockedWheel",
49
+ "LockedXcframework",
50
+ "PythonXcframework",
51
+ "LockError",
52
+ "compute_pyproject_sha256",
53
+ "is_in_sync",
54
+ "load",
55
+ "loads",
56
+ "dumps",
57
+ "PipResolver",
58
+ "Resolver",
59
+ "ResolvedPackage",
60
+ "ResolvedWheel",
61
+ "ResolverError",
62
+ "get_resolver",
63
+ "slice_tags",
64
+ "ResolvedSwiftPackage",
65
+ "SpmResolver",
66
+ "SpmResolverError",
67
+ "XcodeSpmResolver",
68
+ "get_spm_resolver",
69
+ "XcframeworkResolverError",
70
+ "resolve_xcframeworks",
71
+ ]