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
kivyforge/cli/clean.py ADDED
@@ -0,0 +1,135 @@
1
+ """``kivyforge clean`` — remove generated artifacts (spec 05)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ from pathlib import Path
8
+
9
+ import click
10
+
11
+ from ..artifacts.cache import ArtifactCache
12
+ from ..config import ConfigError, load_config
13
+ from ._common import PYPROJECT_NAME, ToolchainError
14
+
15
+
16
+ @click.command()
17
+ @click.option(
18
+ "--cache",
19
+ "flush_cache",
20
+ is_flag=True,
21
+ help="Also flush the artifact download cache and the generated Android "
22
+ "project's own Gradle cache.",
23
+ )
24
+ @click.option(
25
+ "--cache-all",
26
+ "flush_cache_all",
27
+ is_flag=True,
28
+ help="--cache, plus the shared ~/.gradle caches (affects every project on "
29
+ "this machine; the next build re-downloads Gradle's dependencies).",
30
+ )
31
+ @click.option(
32
+ "--project-only",
33
+ is_flag=True,
34
+ default=False,
35
+ help="Only the generated project artifacts, not the artifact cache (default).",
36
+ )
37
+ def clean(flush_cache: bool, flush_cache_all: bool, project_only: bool) -> None:
38
+ """Remove generated artifacts in the project folder."""
39
+ cwd = Path.cwd()
40
+ pyproject = cwd / PYPROJECT_NAME
41
+ if flush_cache_all and project_only:
42
+ raise ToolchainError(
43
+ "--project-only and --cache-all contradict each other; pass one."
44
+ )
45
+ flush_cache = flush_cache or flush_cache_all
46
+
47
+ if pyproject.is_file():
48
+ try:
49
+ # A desktop-only project has no [tool.kivy.ios] overlay; clean must
50
+ # work for any target, so it requires no platform overlay at all.
51
+ config = load_config(pyproject, require_ios=False)
52
+ except ConfigError as exc:
53
+ raise ToolchainError(exc.format()) from exc
54
+ android_project = cwd / f"{config.app_slug}-android"
55
+ # Always before the removal below: a live Gradle daemon holds handles
56
+ # under app/build, which makes rmtree fail outright on Windows.
57
+ _stop_gradle_daemon(android_project)
58
+ # Every platform's generated staging/output trees: iOS <app>-ios/,
59
+ # macOS build/macos/, Linux build/linux/ + dist/linux/, and Windows
60
+ # build/windows/ + dist/windows/.
61
+ targets = [
62
+ cwd / f"{config.app_slug}-ios",
63
+ android_project,
64
+ cwd / "build" / "macos",
65
+ cwd / "build" / "linux",
66
+ cwd / "dist" / "linux",
67
+ cwd / "build" / "windows",
68
+ cwd / "dist" / "windows",
69
+ ]
70
+ removed = [t for t in targets if _remove(t)]
71
+ # Drop build/ and dist/ if emptied so no stray husks are left behind.
72
+ for parent in (cwd / "build", cwd / "dist"):
73
+ _remove_if_empty(parent)
74
+ if removed:
75
+ for t in removed:
76
+ click.echo(f"Removed {t.relative_to(cwd).as_posix()}/")
77
+ else:
78
+ click.echo("Nothing to clean (no generated artifacts found).")
79
+ elif not flush_cache:
80
+ raise ToolchainError(
81
+ f"no {PYPROJECT_NAME} found in the current directory.\n"
82
+ " Run clean from your project directory, or pass --cache to flush "
83
+ "only the global artifact cache."
84
+ )
85
+
86
+ if flush_cache:
87
+ cache = ArtifactCache()
88
+ cache.clear()
89
+ click.echo("Flushed the artifact download cache.")
90
+ if flush_cache_all:
91
+ _flush_shared_gradle(cwd)
92
+
93
+
94
+ def _stop_gradle_daemon(android_project: Path) -> None:
95
+ if not android_project.is_dir():
96
+ return
97
+ from ..platforms.android.gradlew import stop_gradle_daemon
98
+
99
+ if stop_gradle_daemon(android_project):
100
+ click.echo("Stopped the project's Gradle daemon(s).")
101
+
102
+
103
+ def _flush_shared_gradle(cwd: Path) -> None:
104
+ """Clear the shared ``~/.gradle`` caches ``--cache`` deliberately spares.
105
+
106
+ Only the cache subtrees, not all of ``~/.gradle``: the wrapper distributions
107
+ and any ``gradle.properties`` there are configuration, and blowing them away
108
+ would cost every project on the machine a re-download for no benefit.
109
+ """
110
+ gradle_home = Path(
111
+ os.environ.get("GRADLE_USER_HOME") or (Path.home() / ".gradle")
112
+ ).expanduser()
113
+ if gradle_home == cwd or not gradle_home.is_dir():
114
+ return
115
+ removed = [
116
+ sub.name
117
+ for sub in (gradle_home / "caches", gradle_home / "daemon")
118
+ if _remove(sub)
119
+ ]
120
+ if removed:
121
+ click.echo(f"Flushed shared Gradle {', '.join(removed)} under {gradle_home}.")
122
+ else:
123
+ click.echo(f"No shared Gradle caches to flush under {gradle_home}.")
124
+
125
+
126
+ def _remove(path: Path) -> bool:
127
+ if path.is_dir():
128
+ shutil.rmtree(path)
129
+ return True
130
+ return False
131
+
132
+
133
+ def _remove_if_empty(path: Path) -> None:
134
+ if path.is_dir() and not any(path.iterdir()):
135
+ path.rmdir()
@@ -0,0 +1,52 @@
1
+ """``kivyforge doctor`` — environment + project health check (spec 05).
2
+
3
+ Platform-aware: resolves the target (``-p`` / ``KIVYFORGE_PLATFORM`` / host,
4
+ falling back to iOS for a bare environment check) and dispatches to that
5
+ backend's ``doctor`` over its ``pyproject.toml`` + ``pylock.<platform>.toml``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+
13
+ import click
14
+
15
+ from .. import __version__
16
+ from ..doctor import Status, worst_status
17
+ from ..platforms import Platform, PlatformResolutionError, get_platform
18
+ from ..platforms import resolve_target as _resolve_platform
19
+ from ._common import PYPROJECT_NAME
20
+ from ._platform import configured_platforms, platform_option
21
+
22
+
23
+ @click.command()
24
+ @platform_option
25
+ @click.option("--offline", is_flag=True, help="Skip network-dependent checks.")
26
+ def doctor(cli_platform: str | None, offline: bool) -> None:
27
+ """Run environment and project health checks."""
28
+ cwd = Path.cwd()
29
+ backend = _resolve_doctor_backend(cli_platform, cwd)
30
+ results = backend.doctor(cwd, kivyforge_version=__version__, offline=offline)
31
+
32
+ mode = "project" if _has_config(cwd) else "environment"
33
+ click.echo(f"kivyforge doctor ({backend.name}, {mode} mode)\n")
34
+ for result in results:
35
+ click.echo(result.render())
36
+
37
+ if worst_status(results) is Status.FAIL:
38
+ raise SystemExit(1)
39
+
40
+
41
+ def _resolve_doctor_backend(cli_platform: str | None, cwd: Path) -> Platform:
42
+ pyproject = cwd / PYPROJECT_NAME
43
+ configured = configured_platforms(pyproject) if pyproject.is_file() else set()
44
+ try:
45
+ return _resolve_platform(cli_platform, configured=configured, env=os.environ)
46
+ except PlatformResolutionError:
47
+ # Environment-mode fallback: no project/target to infer from.
48
+ return get_platform("ios")
49
+
50
+
51
+ def _has_config(cwd: Path) -> bool:
52
+ return (cwd / PYPROJECT_NAME).is_file()