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.
- kivyforge/__init__.py +9 -0
- kivyforge/artifacts/__init__.py +41 -0
- kivyforge/artifacts/cache.py +81 -0
- kivyforge/artifacts/collect.py +354 -0
- kivyforge/artifacts/download.py +125 -0
- kivyforge/artifacts/frameworks.py +240 -0
- kivyforge/artifacts/native_stage_util.py +346 -0
- kivyforge/artifacts/runtime.py +65 -0
- kivyforge/artifacts/verify.py +45 -0
- kivyforge/artifacts/wheels.py +89 -0
- kivyforge/bundle/__init__.py +1 -0
- kivyforge/bundle/pycompile.py +161 -0
- kivyforge/cli/__init__.py +52 -0
- kivyforge/cli/_common.py +62 -0
- kivyforge/cli/_platform.py +84 -0
- kivyforge/cli/build.py +111 -0
- kivyforge/cli/clean.py +135 -0
- kivyforge/cli/doctor.py +52 -0
- kivyforge/cli/init.py +612 -0
- kivyforge/cli/init_writer.py +716 -0
- kivyforge/cli/lock.py +261 -0
- kivyforge/cli/open_cmd.py +15 -0
- kivyforge/cli/package.py +134 -0
- kivyforge/cli/run.py +162 -0
- kivyforge/cli/status.py +20 -0
- kivyforge/cli/upgrade.py +314 -0
- kivyforge/config/__init__.py +46 -0
- kivyforge/config/errors.py +43 -0
- kivyforge/config/icons.py +60 -0
- kivyforge/config/loader.py +3079 -0
- kivyforge/config/model.py +817 -0
- kivyforge/doctor/__init__.py +19 -0
- kivyforge/doctor/checks_common.py +95 -0
- kivyforge/doctor/probe.py +357 -0
- kivyforge/doctor/result.py +41 -0
- kivyforge/lock/__init__.py +42 -0
- kivyforge/lock/_pip_shim.py +63 -0
- kivyforge/lock/find_links.py +182 -0
- kivyforge/lock/model.py +69 -0
- kivyforge/lock/pep751.py +111 -0
- kivyforge/lock/reader.py +30 -0
- kivyforge/lock/resolver.py +124 -0
- kivyforge/lock/wheelruntime/__init__.py +70 -0
- kivyforge/lock/wheelruntime/builder.py +280 -0
- kivyforge/lock/wheelruntime/model.py +94 -0
- kivyforge/lock/wheelruntime/native_binaries.py +127 -0
- kivyforge/lock/wheelruntime/pbs_github.py +133 -0
- kivyforge/lock/wheelruntime/profile.py +104 -0
- kivyforge/lock/wheelruntime/resolver.py +248 -0
- kivyforge/lock/wheelruntime/runtime.py +161 -0
- kivyforge/lock/wheelruntime/serialize.py +245 -0
- kivyforge/platforms/__init__.py +114 -0
- kivyforge/platforms/android/__init__.py +140 -0
- kivyforge/platforms/android/adb.py +215 -0
- kivyforge/platforms/android/bootstrap/__init__.py +7 -0
- kivyforge/platforms/android/bootstrap/contract.py +166 -0
- kivyforge/platforms/android/bootstrap/render.py +244 -0
- kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
- kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
- kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
- kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
- kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
- kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
- kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
- kivyforge/platforms/android/cli.py +1023 -0
- kivyforge/platforms/android/doctor.py +899 -0
- kivyforge/platforms/android/elf.py +148 -0
- kivyforge/platforms/android/generate/__init__.py +1 -0
- kivyforge/platforms/android/generate/manifest.py +261 -0
- kivyforge/platforms/android/generate/project.py +462 -0
- kivyforge/platforms/android/generate/services.py +187 -0
- kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
- kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
- kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
- kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
- kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
- kivyforge/platforms/android/gradlew.py +60 -0
- kivyforge/platforms/android/icons.py +237 -0
- kivyforge/platforms/android/lock/__init__.py +1 -0
- kivyforge/platforms/android/lock/archives.py +109 -0
- kivyforge/platforms/android/lock/builder.py +387 -0
- kivyforge/platforms/android/lock/markers.py +71 -0
- kivyforge/platforms/android/lock/maven.py +252 -0
- kivyforge/platforms/android/lock/model.py +113 -0
- kivyforge/platforms/android/lock/python_meta.py +199 -0
- kivyforge/platforms/android/lock/reader.py +206 -0
- kivyforge/platforms/android/lock/resolver.py +276 -0
- kivyforge/platforms/android/lock/writer.py +121 -0
- kivyforge/platforms/android/policy.py +214 -0
- kivyforge/platforms/android/signing.py +145 -0
- kivyforge/platforms/android/smoke.py +43 -0
- kivyforge/platforms/android/splash.py +293 -0
- kivyforge/platforms/android/stage/__init__.py +1 -0
- kivyforge/platforms/android/stage/bundle.py +295 -0
- kivyforge/platforms/android/stage/jnilibs.py +186 -0
- kivyforge/platforms/android/stage/runtime.py +61 -0
- kivyforge/platforms/android/stage/wheels.py +106 -0
- kivyforge/platforms/android/toolchain.py +29 -0
- kivyforge/platforms/base.py +157 -0
- kivyforge/platforms/ios/__init__.py +129 -0
- kivyforge/platforms/ios/assets.py +193 -0
- kivyforge/platforms/ios/buildsettings.py +128 -0
- kivyforge/platforms/ios/cli.py +543 -0
- kivyforge/platforms/ios/doctor.py +410 -0
- kivyforge/platforms/ios/entitlements.py +34 -0
- kivyforge/platforms/ios/generator.py +318 -0
- kivyforge/platforms/ios/lock/__init__.py +71 -0
- kivyforge/platforms/ios/lock/builder.py +393 -0
- kivyforge/platforms/ios/lock/markers.py +69 -0
- kivyforge/platforms/ios/lock/model.py +95 -0
- kivyforge/platforms/ios/lock/python_meta.py +187 -0
- kivyforge/platforms/ios/lock/reader.py +191 -0
- kivyforge/platforms/ios/lock/resolver.py +304 -0
- kivyforge/platforms/ios/lock/spm.py +217 -0
- kivyforge/platforms/ios/lock/writer.py +114 -0
- kivyforge/platforms/ios/lock/xcframework.py +188 -0
- kivyforge/platforms/ios/materialize.py +59 -0
- kivyforge/platforms/ios/plist.py +91 -0
- kivyforge/platforms/ios/privacy.py +39 -0
- kivyforge/platforms/ios/skeleton.py +165 -0
- kivyforge/platforms/ios/sources.py +63 -0
- kivyforge/platforms/ios/staging.py +225 -0
- kivyforge/platforms/ios/swift_packages.py +324 -0
- kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
- kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
- kivyforge/platforms/ios/templates/main.m +15 -0
- kivyforge/platforms/ios/templates/main_config.h.in +12 -0
- kivyforge/platforms/ios/xcframeworks.py +194 -0
- kivyforge/platforms/ios/xcode/__init__.py +76 -0
- kivyforge/platforms/ios/xcode/commands.py +296 -0
- kivyforge/platforms/ios/xcode/runner.py +316 -0
- kivyforge/platforms/linux/__init__.py +120 -0
- kivyforge/platforms/linux/appimage.py +169 -0
- kivyforge/platforms/linux/bundle.py +235 -0
- kivyforge/platforms/linux/cli.py +232 -0
- kivyforge/platforms/linux/desktop.py +71 -0
- kivyforge/platforms/linux/doctor.py +438 -0
- kivyforge/platforms/linux/elftools.py +95 -0
- kivyforge/platforms/linux/icons.py +129 -0
- kivyforge/platforms/linux/launcher.py +107 -0
- kivyforge/platforms/linux/lock/__init__.py +126 -0
- kivyforge/platforms/linux/lock/profile.py +254 -0
- kivyforge/platforms/linux/lock/runtime.py +75 -0
- kivyforge/platforms/linux/native_stage.py +47 -0
- kivyforge/platforms/linux/runtime_stage.py +107 -0
- kivyforge/platforms/linux/wheels_stage.py +103 -0
- kivyforge/platforms/macos/__init__.py +121 -0
- kivyforge/platforms/macos/bundle.py +255 -0
- kivyforge/platforms/macos/cli.py +269 -0
- kivyforge/platforms/macos/doctor.py +480 -0
- kivyforge/platforms/macos/icns.py +75 -0
- kivyforge/platforms/macos/launcher.py +118 -0
- kivyforge/platforms/macos/lock/__init__.py +112 -0
- kivyforge/platforms/macos/lock/profile.py +119 -0
- kivyforge/platforms/macos/lock/runtime.py +58 -0
- kivyforge/platforms/macos/machotools.py +138 -0
- kivyforge/platforms/macos/native_stage.py +47 -0
- kivyforge/platforms/macos/notarize.py +143 -0
- kivyforge/platforms/macos/plist.py +37 -0
- kivyforge/platforms/macos/runtime_stage.py +110 -0
- kivyforge/platforms/macos/signing.py +78 -0
- kivyforge/platforms/macos/wheels_stage.py +108 -0
- kivyforge/platforms/windows/__init__.py +133 -0
- kivyforge/platforms/windows/assets.py +76 -0
- kivyforge/platforms/windows/bundle.py +298 -0
- kivyforge/platforms/windows/cli.py +314 -0
- kivyforge/platforms/windows/doctor.py +563 -0
- kivyforge/platforms/windows/fsswap.py +87 -0
- kivyforge/platforms/windows/icons.py +64 -0
- kivyforge/platforms/windows/launcher/__init__.py +193 -0
- kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
- kivyforge/platforms/windows/launcher/launcher.c +379 -0
- kivyforge/platforms/windows/lock/__init__.py +112 -0
- kivyforge/platforms/windows/lock/profile.py +101 -0
- kivyforge/platforms/windows/lock/runtime.py +64 -0
- kivyforge/platforms/windows/naming.py +52 -0
- kivyforge/platforms/windows/native_stage.py +66 -0
- kivyforge/platforms/windows/petools.py +98 -0
- kivyforge/platforms/windows/rcedit.py +116 -0
- kivyforge/platforms/windows/runtime_stage.py +221 -0
- kivyforge/platforms/windows/signing.py +168 -0
- kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
- kivyforge/platforms/windows/vendor/NOTICE +17 -0
- kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
- kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
- kivyforge/platforms/windows/vendor/__init__.py +12 -0
- kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
- kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
- kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
- kivyforge/platforms/windows/wheels_stage.py +139 -0
- kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
- kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
- kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
- kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
- kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
- kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""The ``Platform`` backend interface (common design doc 05).
|
|
2
|
+
|
|
3
|
+
A platform backend describes one build target (``ios``, ``macos``, ...) and owns
|
|
4
|
+
its verb behavior: metadata, host mapping, host-capability, package formats, and
|
|
5
|
+
the ``build``/``run``/``package``/``doctor`` implementations. The ``cli`` verbs
|
|
6
|
+
resolve the target and dispatch to these methods, so adding a platform is a
|
|
7
|
+
matter of registering one ``Platform`` subclass — the verbs stay backend-agnostic
|
|
8
|
+
(see ``docs/design/common/05-platform-architecture.md``).
|
|
9
|
+
|
|
10
|
+
Each verb method lazily imports its backend's implementation module
|
|
11
|
+
(``platforms/<os>/cli.py`` / ``doctor.py``) so importing the backend class stays
|
|
12
|
+
cheap and cycle-free (registry -> backend -> bundler/lock/cli is only paid when a
|
|
13
|
+
verb actually runs).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from kivyforge.doctor.result import CheckResult
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HostCapabilityError(Exception):
|
|
28
|
+
"""The current host cannot build for this target (e.g. iOS off macOS)."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Platform(ABC):
|
|
32
|
+
"""Describes a single build target and its host requirements."""
|
|
33
|
+
|
|
34
|
+
#: Canonical platform name, e.g. ``"ios"``. Also the ``pylock.<name>.toml``
|
|
35
|
+
#: infix and the ``[tool.kivy.<name>]`` overlay key.
|
|
36
|
+
name: str
|
|
37
|
+
|
|
38
|
+
#: Accepted CLI/env aliases in addition to ``name``.
|
|
39
|
+
aliases: tuple[str, ...] = ()
|
|
40
|
+
|
|
41
|
+
#: The ``platform.system()`` value for which this platform is the *host
|
|
42
|
+
#: default* (resolution-chain step 3). Desktop targets set their own OS
|
|
43
|
+
#: (macOS -> ``"Darwin"``); cross-compiled targets (iOS, Android) set
|
|
44
|
+
#: ``None`` because no host maps to them — they always need explicit
|
|
45
|
+
#: selection.
|
|
46
|
+
host_system: str | None = None
|
|
47
|
+
|
|
48
|
+
#: Distributable artifact shapes for ``package -f`` (first = default).
|
|
49
|
+
package_formats: tuple[str, ...] = ()
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def default_package_format(self) -> str | None:
|
|
53
|
+
return self.package_formats[0] if self.package_formats else None
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def selectors(self) -> tuple[str, ...]:
|
|
57
|
+
"""All names that select this platform (``name`` + ``aliases``)."""
|
|
58
|
+
return (self.name, *self.aliases)
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def check_host_capability(self, *, host_system: str | None = None) -> None:
|
|
62
|
+
"""Raise :class:`HostCapabilityError` if this host cannot build the target.
|
|
63
|
+
|
|
64
|
+
``host_system`` defaults to the real host (``platform.system()``); it is
|
|
65
|
+
injectable so the check is unit-testable off the target host.
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
# -- verb dispatch ----------------------------------------------------- #
|
|
70
|
+
#
|
|
71
|
+
# Default implementations raise so an unimplemented verb surfaces clearly.
|
|
72
|
+
# Every registered backend overrides the verbs it supports; the ``cli`` verbs
|
|
73
|
+
# pass the full option superset and each backend takes what it needs.
|
|
74
|
+
#
|
|
75
|
+
# Options that only one platform acts on (``debug``/``fmt``/``abi`` and the
|
|
76
|
+
# Android keystore overrides) are keyword-with-default: the CLI rejects them
|
|
77
|
+
# for the platforms they do not apply to, so those backends accept and
|
|
78
|
+
# ignore them rather than each verb growing a per-platform call shape.
|
|
79
|
+
|
|
80
|
+
def build(
|
|
81
|
+
self,
|
|
82
|
+
project_root: Path,
|
|
83
|
+
*,
|
|
84
|
+
target: str | None,
|
|
85
|
+
arch: str | None,
|
|
86
|
+
no_verify_lock: bool,
|
|
87
|
+
no_cache: bool,
|
|
88
|
+
team_id: str | None,
|
|
89
|
+
signing_identity: str | None,
|
|
90
|
+
export_method: str,
|
|
91
|
+
debug: bool = False,
|
|
92
|
+
fmt: str | None = None,
|
|
93
|
+
abi: str | None = None,
|
|
94
|
+
) -> None:
|
|
95
|
+
raise NotImplementedError(f"build is not supported for {self.name!r}.")
|
|
96
|
+
|
|
97
|
+
def run(
|
|
98
|
+
self,
|
|
99
|
+
project_root: Path,
|
|
100
|
+
*,
|
|
101
|
+
target: str,
|
|
102
|
+
arch: str | None,
|
|
103
|
+
destination: str | None,
|
|
104
|
+
no_build: bool,
|
|
105
|
+
) -> None:
|
|
106
|
+
raise NotImplementedError(f"run is not supported for {self.name!r}.")
|
|
107
|
+
|
|
108
|
+
def package(
|
|
109
|
+
self,
|
|
110
|
+
project_root: Path,
|
|
111
|
+
*,
|
|
112
|
+
fmt: str,
|
|
113
|
+
arch: str | None,
|
|
114
|
+
team_id: str | None,
|
|
115
|
+
signing_identity: str | None,
|
|
116
|
+
export_method: str,
|
|
117
|
+
notarize: bool | None,
|
|
118
|
+
notary_profile: str | None,
|
|
119
|
+
no_verify_lock: bool,
|
|
120
|
+
no_cache: bool,
|
|
121
|
+
abi: str | None = None,
|
|
122
|
+
keystore: str | None = None,
|
|
123
|
+
key_alias: str | None = None,
|
|
124
|
+
) -> None:
|
|
125
|
+
raise NotImplementedError(f"package is not supported for {self.name!r}.")
|
|
126
|
+
|
|
127
|
+
def open_project(self, project_root: Path) -> None:
|
|
128
|
+
from kivyforge.cli._common import ToolchainError
|
|
129
|
+
|
|
130
|
+
raise ToolchainError(
|
|
131
|
+
f"`open` is an iOS/Xcode command; {self.name} has no project to open."
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def status(self, project_root: Path) -> None:
|
|
135
|
+
raise NotImplementedError(f"status is not supported for {self.name!r}.")
|
|
136
|
+
|
|
137
|
+
def doctor(
|
|
138
|
+
self, cwd: Path, *, kivyforge_version: str, offline: bool
|
|
139
|
+
) -> list[CheckResult]:
|
|
140
|
+
raise NotImplementedError(f"doctor is not supported for {self.name!r}.")
|
|
141
|
+
|
|
142
|
+
def reject_ios_only_target(self, target: str | None) -> None:
|
|
143
|
+
"""Desktop backends reject the iOS-only ``--simulator/--device/--release``."""
|
|
144
|
+
if target is None:
|
|
145
|
+
return
|
|
146
|
+
from kivyforge.cli._common import ToolchainError
|
|
147
|
+
|
|
148
|
+
artifact = {"macos": ".app", "linux": "AppDir", "windows": "onedir folder"}.get(
|
|
149
|
+
self.name, "bundle"
|
|
150
|
+
)
|
|
151
|
+
raise ToolchainError(
|
|
152
|
+
f"--{target} is an iOS target; {self.name} has no simulator/device/"
|
|
153
|
+
"release targets.\n"
|
|
154
|
+
f" Use `kivyforge build -p {self.name}` (optionally --arch) to build "
|
|
155
|
+
f"the {artifact}, or `kivyforge package -p {self.name}` for the "
|
|
156
|
+
"distributable."
|
|
157
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""iOS platform backend: metadata, host capability, and verb dispatch.
|
|
2
|
+
|
|
3
|
+
The iOS build/run/package/open/status/doctor logic lives in ``.cli`` and
|
|
4
|
+
``.doctor``; the verb methods here lazily import them so importing the backend
|
|
5
|
+
class stays cheap and cycle-free.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import platform as _platform
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from ..base import HostCapabilityError, Platform
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from kivyforge.doctor.result import CheckResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class IosPlatform(Platform):
|
|
21
|
+
name = "ios"
|
|
22
|
+
# iOS is cross-compiled from macOS: no host maps to it, so it is never a
|
|
23
|
+
# host default and must be selected explicitly (-p ios / KIVYFORGE_PLATFORM).
|
|
24
|
+
host_system = None
|
|
25
|
+
# The distributable is the Xcode-built app / .ipa (App Store submission is
|
|
26
|
+
# external). Only one shape today, so `-f` defaults to it.
|
|
27
|
+
package_formats = ("ipa",)
|
|
28
|
+
|
|
29
|
+
def check_host_capability(self, *, host_system: str | None = None) -> None:
|
|
30
|
+
host = host_system if host_system is not None else _platform.system()
|
|
31
|
+
if host != "Darwin":
|
|
32
|
+
raise HostCapabilityError(
|
|
33
|
+
"building for iOS requires macOS with Xcode; "
|
|
34
|
+
f"this host is {host!r}.\n"
|
|
35
|
+
" iOS wheels and the Python.xcframework are cross-built on a "
|
|
36
|
+
"macOS host — there is no on-device or off-macOS build path.\n"
|
|
37
|
+
" Run `kivyforge doctor -p ios` on a Mac to check Xcode setup."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def build(
|
|
41
|
+
self,
|
|
42
|
+
project_root: Path,
|
|
43
|
+
*,
|
|
44
|
+
target: str | None,
|
|
45
|
+
arch: str | None,
|
|
46
|
+
no_verify_lock: bool,
|
|
47
|
+
no_cache: bool,
|
|
48
|
+
team_id: str | None,
|
|
49
|
+
signing_identity: str | None,
|
|
50
|
+
export_method: str,
|
|
51
|
+
debug: bool = False,
|
|
52
|
+
fmt: str | None = None,
|
|
53
|
+
abi: str | None = None,
|
|
54
|
+
) -> None:
|
|
55
|
+
from .cli import ios_build
|
|
56
|
+
|
|
57
|
+
ios_build(
|
|
58
|
+
project_root,
|
|
59
|
+
target=target,
|
|
60
|
+
arch=arch,
|
|
61
|
+
no_verify_lock=no_verify_lock,
|
|
62
|
+
no_cache=no_cache,
|
|
63
|
+
team_id=team_id,
|
|
64
|
+
signing_identity=signing_identity,
|
|
65
|
+
export_method=export_method,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def run(
|
|
69
|
+
self,
|
|
70
|
+
project_root: Path,
|
|
71
|
+
*,
|
|
72
|
+
target: str,
|
|
73
|
+
arch: str | None,
|
|
74
|
+
destination: str | None,
|
|
75
|
+
no_build: bool,
|
|
76
|
+
) -> None:
|
|
77
|
+
from .cli import ios_run
|
|
78
|
+
|
|
79
|
+
ios_run(
|
|
80
|
+
project_root,
|
|
81
|
+
target=target,
|
|
82
|
+
destination=destination,
|
|
83
|
+
no_build=no_build,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def package(
|
|
87
|
+
self,
|
|
88
|
+
project_root: Path,
|
|
89
|
+
*,
|
|
90
|
+
fmt: str,
|
|
91
|
+
arch: str | None,
|
|
92
|
+
team_id: str | None,
|
|
93
|
+
signing_identity: str | None,
|
|
94
|
+
export_method: str,
|
|
95
|
+
notarize: bool | None,
|
|
96
|
+
notary_profile: str | None,
|
|
97
|
+
no_verify_lock: bool,
|
|
98
|
+
no_cache: bool,
|
|
99
|
+
abi: str | None = None,
|
|
100
|
+
keystore: str | None = None,
|
|
101
|
+
key_alias: str | None = None,
|
|
102
|
+
) -> None:
|
|
103
|
+
from .cli import ios_package
|
|
104
|
+
|
|
105
|
+
ios_package(
|
|
106
|
+
project_root,
|
|
107
|
+
team_id=team_id,
|
|
108
|
+
signing_identity=signing_identity,
|
|
109
|
+
export_method=export_method,
|
|
110
|
+
no_verify_lock=no_verify_lock,
|
|
111
|
+
no_cache=no_cache,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def open_project(self, project_root: Path) -> None:
|
|
115
|
+
from .cli import ios_open
|
|
116
|
+
|
|
117
|
+
ios_open(project_root)
|
|
118
|
+
|
|
119
|
+
def status(self, project_root: Path) -> None:
|
|
120
|
+
from .cli import ios_status
|
|
121
|
+
|
|
122
|
+
ios_status(project_root)
|
|
123
|
+
|
|
124
|
+
def doctor(
|
|
125
|
+
self, cwd: Path, *, kivyforge_version: str, offline: bool
|
|
126
|
+
) -> list[CheckResult]:
|
|
127
|
+
from .doctor import ios_doctor
|
|
128
|
+
|
|
129
|
+
return ios_doctor(cwd, kivyforge_version=kivyforge_version, offline=offline)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Generate iOS app icons, splash assets, and launch screen (spec 01 / 06).
|
|
2
|
+
|
|
3
|
+
Uses Xcode 14+ single-size ``AppIcon`` (1024x1024; Xcode scales at build time)
|
|
4
|
+
and a ``LaunchScreen.storyboard`` with an optional ``Splash`` image set — not the
|
|
5
|
+
deprecated ``LaunchImage.launchimage`` catalog.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import shutil
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from kivyforge.config.icons import validate_icon_source
|
|
16
|
+
from kivyforge.config.model import Config
|
|
17
|
+
|
|
18
|
+
_DEFAULT_SPLASH_BACKGROUND = "#ffffff"
|
|
19
|
+
_HEX_COLOR = re.compile(r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def catalog_needed(config: Config) -> bool:
|
|
23
|
+
"""True when ``Assets.xcassets`` should be emitted."""
|
|
24
|
+
ios = config.ios
|
|
25
|
+
return bool(ios and (ios.icons.source or ios.splash.source))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def launch_screen_needed(config: Config) -> bool:
|
|
29
|
+
"""True when ``LaunchScreen.storyboard`` should be emitted."""
|
|
30
|
+
ios = config.ios
|
|
31
|
+
return bool(ios and (ios.splash.source or ios.splash.background))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def generate_asset_catalog(
|
|
35
|
+
config: Config, resources_dir: str | Path, *, project_root: Path | None = None
|
|
36
|
+
) -> Path | None:
|
|
37
|
+
"""Create ``Assets.xcassets`` when icons and/or splash image are configured.
|
|
38
|
+
|
|
39
|
+
Returns the catalog path, or None when no catalog entries are needed.
|
|
40
|
+
"""
|
|
41
|
+
if not catalog_needed(config):
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
assert config.ios is not None
|
|
45
|
+
root = project_root or Path.cwd()
|
|
46
|
+
catalog = Path(resources_dir) / "Assets.xcassets"
|
|
47
|
+
catalog.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
_write_catalog_root(catalog)
|
|
49
|
+
|
|
50
|
+
if config.ios.icons.source:
|
|
51
|
+
_generate_app_icon(catalog, (root / config.ios.icons.source).resolve())
|
|
52
|
+
if config.ios.splash.source:
|
|
53
|
+
_generate_splash_imageset(catalog, (root / config.ios.splash.source).resolve())
|
|
54
|
+
return catalog
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def write_launch_screen(
|
|
58
|
+
config: Config, project_dir: str | Path, *, project_root: Path | None = None
|
|
59
|
+
) -> Path | None:
|
|
60
|
+
"""Write ``LaunchScreen.storyboard`` when splash is configured."""
|
|
61
|
+
if not launch_screen_needed(config):
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
assert config.ios is not None
|
|
65
|
+
project_dir = Path(project_dir)
|
|
66
|
+
background = config.ios.splash.background or _DEFAULT_SPLASH_BACKGROUND
|
|
67
|
+
rgb = _parse_hex_color(background)
|
|
68
|
+
has_image = bool(config.ios.splash.source)
|
|
69
|
+
|
|
70
|
+
path = project_dir / "LaunchScreen.storyboard"
|
|
71
|
+
path.write_text(_launch_screen_xml(rgb=rgb, has_image=has_image), encoding="utf-8")
|
|
72
|
+
return path
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _write_catalog_root(catalog: Path) -> None:
|
|
76
|
+
(catalog / "Contents.json").write_text(
|
|
77
|
+
json.dumps({"info": {"author": "kivyforge", "version": 1}}, indent=2),
|
|
78
|
+
encoding="utf-8",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _generate_app_icon(catalog: Path, source: Path) -> None:
|
|
83
|
+
validate_icon_source(source)
|
|
84
|
+
|
|
85
|
+
appicon_dir = catalog / "AppIcon.appiconset"
|
|
86
|
+
appicon_dir.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
contents = {
|
|
89
|
+
"images": [
|
|
90
|
+
{
|
|
91
|
+
"filename": "AppIcon.png",
|
|
92
|
+
"idiom": "universal",
|
|
93
|
+
"platform": "ios",
|
|
94
|
+
"size": "1024x1024",
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"info": {"author": "xcode", "version": 1},
|
|
98
|
+
}
|
|
99
|
+
(appicon_dir / "Contents.json").write_text(
|
|
100
|
+
json.dumps(contents, indent=2), encoding="utf-8"
|
|
101
|
+
)
|
|
102
|
+
shutil.copy2(source, appicon_dir / "AppIcon.png")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _generate_splash_imageset(catalog: Path, source: Path) -> None:
|
|
106
|
+
imageset = catalog / "Splash.imageset"
|
|
107
|
+
imageset.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
# Single unscaled "universal" image (one high-res asset Xcode scales as
|
|
109
|
+
# needed), matching the AppIcon set. Declaring 1x/2x/3x slots but filling
|
|
110
|
+
# only 1x left empty 2x/3x slots in Xcode.
|
|
111
|
+
contents = {
|
|
112
|
+
"images": [
|
|
113
|
+
{
|
|
114
|
+
"filename": "Splash.png",
|
|
115
|
+
"idiom": "universal",
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"info": {"author": "xcode", "version": 1},
|
|
119
|
+
}
|
|
120
|
+
(imageset / "Contents.json").write_text(
|
|
121
|
+
json.dumps(contents, indent=2), encoding="utf-8"
|
|
122
|
+
)
|
|
123
|
+
shutil.copy2(source, imageset / "Splash.png")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _parse_hex_color(value: str) -> tuple[float, float, float]:
|
|
127
|
+
if not _HEX_COLOR.match(value):
|
|
128
|
+
raise ValueError(f"invalid hex color {value!r}; expected #rrggbb or #rgb")
|
|
129
|
+
digits = value[1:]
|
|
130
|
+
if len(digits) == 3:
|
|
131
|
+
digits = "".join(ch * 2 for ch in digits)
|
|
132
|
+
return (
|
|
133
|
+
int(digits[0:2], 16) / 255.0,
|
|
134
|
+
int(digits[2:4], 16) / 255.0,
|
|
135
|
+
int(digits[4:6], 16) / 255.0,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _launch_screen_xml(*, rgb: tuple[float, float, float], has_image: bool) -> str:
|
|
140
|
+
r, g, b = rgb
|
|
141
|
+
color = (
|
|
142
|
+
f'<color key="backgroundColor" red="{r:.6f}" green="{g:.6f}" '
|
|
143
|
+
f'blue="{b:.6f}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>'
|
|
144
|
+
)
|
|
145
|
+
image_block = ""
|
|
146
|
+
image_constraints = ""
|
|
147
|
+
resources_block = ""
|
|
148
|
+
if has_image:
|
|
149
|
+
image_block = """
|
|
150
|
+
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" translatesAutoresizingMaskIntoConstraints="NO" id="splash-image">
|
|
151
|
+
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
|
152
|
+
</imageView>"""
|
|
153
|
+
image_constraints = """
|
|
154
|
+
<constraint firstItem="splash-image" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="c1"/>
|
|
155
|
+
<constraint firstItem="splash-image" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="c2"/>
|
|
156
|
+
<constraint firstItem="splash-image" firstAttribute="width" relation="lessThanOrEqual" secondItem="Ze5-6b-2t3" secondAttribute="width" id="c3"/>
|
|
157
|
+
<constraint firstItem="splash-image" firstAttribute="height" relation="lessThanOrEqual" secondItem="Ze5-6b-2t3" secondAttribute="height" id="c4"/>"""
|
|
158
|
+
resources_block = """
|
|
159
|
+
<resources>
|
|
160
|
+
<image name="Splash" width="128" height="128"/>
|
|
161
|
+
</resources>"""
|
|
162
|
+
|
|
163
|
+
return f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
164
|
+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
165
|
+
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
|
166
|
+
<dependencies>
|
|
167
|
+
<deployment identifier="iOS"/>
|
|
168
|
+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
|
|
169
|
+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
170
|
+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
171
|
+
</dependencies>
|
|
172
|
+
<scenes>
|
|
173
|
+
<scene sceneID="EHf-IW-A2E">
|
|
174
|
+
<objects>
|
|
175
|
+
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
176
|
+
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
177
|
+
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
|
178
|
+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
179
|
+
<subviews>{image_block}
|
|
180
|
+
</subviews>
|
|
181
|
+
<viewLayoutGuide key="safeArea" id="safe-area"/>
|
|
182
|
+
{color}
|
|
183
|
+
<constraints>{image_constraints}
|
|
184
|
+
</constraints>
|
|
185
|
+
</view>
|
|
186
|
+
</viewController>
|
|
187
|
+
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
188
|
+
</objects>
|
|
189
|
+
<point key="canvasLocation" x="53" y="375"/>
|
|
190
|
+
</scene>
|
|
191
|
+
</scenes>{resources_block}
|
|
192
|
+
</document>
|
|
193
|
+
"""
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Toolchain-managed Xcode build settings (spec 06 §"Toolchain-managed build settings")."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from kivyforge.config.model import Config
|
|
6
|
+
|
|
7
|
+
from .staging import StagingLayout
|
|
8
|
+
|
|
9
|
+
BUILD_PYTHON_SCRIPT = (
|
|
10
|
+
"set -e\n"
|
|
11
|
+
'UTILS="$PROJECT_DIR/Python.xcframework/build/build_utils.sh"\n'
|
|
12
|
+
'if [ ! -f "$UTILS" ]; then UTILS="$PROJECT_DIR/Python.xcframework/build/utils.sh"; fi\n'
|
|
13
|
+
'source "$UTILS"\n'
|
|
14
|
+
"# Copy the platform-appropriate pip-deps slice into the app bundle so\n"
|
|
15
|
+
"# that device and simulator builds never share compiled extension modules.\n"
|
|
16
|
+
'if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then\n'
|
|
17
|
+
' PIP_DEPS_SRC="$PROJECT_DIR/pip-deps-simulator"\n'
|
|
18
|
+
' COLLECT_HINT="kivyforge build --simulator"\n'
|
|
19
|
+
"else\n"
|
|
20
|
+
' PIP_DEPS_SRC="$PROJECT_DIR/pip-deps-device"\n'
|
|
21
|
+
' COLLECT_HINT="kivyforge build --device"\n'
|
|
22
|
+
"fi\n"
|
|
23
|
+
"# Fail loudly if this platform's slice was never collected. Only one slice\n"
|
|
24
|
+
"# is populated per `kivyforge build/run <target>`, but Xcode picks the slice\n"
|
|
25
|
+
"# from its own destination -- switching the destination to an uncollected\n"
|
|
26
|
+
"# target would otherwise ship an app with no dependencies (crash at launch).\n"
|
|
27
|
+
"# The `.collected` marker is written by collect (kivyforge build/run); it,\n"
|
|
28
|
+
"# not directory emptiness, is the signal -- an empty slice WITH the marker is\n"
|
|
29
|
+
"# valid (an app with no third-party dependencies).\n"
|
|
30
|
+
'if [ ! -f "${PIP_DEPS_SRC}.collected" ]; then\n'
|
|
31
|
+
' echo "error: ${PIP_DEPS_SRC} was never collected for this '
|
|
32
|
+
"platform. Run '$COLLECT_HINT' (or 'kivyforge run') first, then rebuild.\"\n"
|
|
33
|
+
" exit 1\n"
|
|
34
|
+
"fi\n"
|
|
35
|
+
'mkdir -p "$CODESIGNING_FOLDER_PATH/pip-deps"\n'
|
|
36
|
+
'rsync -a --delete "$PIP_DEPS_SRC/" "$CODESIGNING_FOLDER_PATH/pip-deps/"\n'
|
|
37
|
+
"install_python Python.xcframework app pip-deps\n"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def managed_settings(
|
|
42
|
+
config: Config, *, configuration: str, layout: StagingLayout | None = None
|
|
43
|
+
) -> dict[str, str]:
|
|
44
|
+
"""Return the managed build settings for a given configuration (Debug/Release).
|
|
45
|
+
|
|
46
|
+
``ENABLE_TESTABILITY`` and ``DEBUG_INFORMATION_FORMAT`` are per-configuration
|
|
47
|
+
(spec 06); the rest are constant across configs.
|
|
48
|
+
"""
|
|
49
|
+
ios = config.ios
|
|
50
|
+
assert ios is not None
|
|
51
|
+
is_release = configuration.lower() == "release"
|
|
52
|
+
|
|
53
|
+
settings: dict[str, str] = {
|
|
54
|
+
"PRODUCT_BUNDLE_IDENTIFIER": ios.bundle_id,
|
|
55
|
+
"PRODUCT_NAME": "$(TARGET_NAME)",
|
|
56
|
+
"IPHONEOS_DEPLOYMENT_TARGET": ios.deployment_target,
|
|
57
|
+
"TARGETED_DEVICE_FAMILY": "1,2",
|
|
58
|
+
"SDKROOT": "iphoneos",
|
|
59
|
+
"INFOPLIST_FILE": f"{config.app_slug}-Info.plist",
|
|
60
|
+
"ENABLE_USER_SCRIPT_SANDBOXING": "NO",
|
|
61
|
+
"ENABLE_TESTABILITY": "NO" if is_release else "YES",
|
|
62
|
+
"DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym" if is_release else "dwarf",
|
|
63
|
+
"FRAMEWORK_SEARCH_PATHS": "$(PROJECT_DIR)",
|
|
64
|
+
"HEADER_SEARCH_PATHS": _header_search_paths(layout),
|
|
65
|
+
# Embedded dynamic frameworks (Python.xcframework, the SDL3/ANGLE family,
|
|
66
|
+
# wheel-embedded and SPM frameworks) are loaded via @rpath at runtime.
|
|
67
|
+
# Manage the runpath explicitly rather than relying on pbxproj embedding
|
|
68
|
+
# side effects, so any embed path resolves under .app/Frameworks/.
|
|
69
|
+
"LD_RUNPATH_SEARCH_PATHS": "$(inherited) @executable_path/Frameworks",
|
|
70
|
+
# UIKit is always required: SDL apps use it transitively through
|
|
71
|
+
# SDL3.framework; pure-Python apps call UIApplicationMain directly.
|
|
72
|
+
"OTHER_LDFLAGS": "-framework UIKit",
|
|
73
|
+
"GCC_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "NO",
|
|
74
|
+
"ALWAYS_SEARCH_USER_PATHS": "NO",
|
|
75
|
+
# The SDL3 family and Python frameworks ship ad-hoc code-signed; Xcode's
|
|
76
|
+
# built-in default COPY_PHASE_STRIP=YES tries to strip them on embed and
|
|
77
|
+
# warns "not stripping binary because it is signed". Disable it (Xcode's
|
|
78
|
+
# own project templates also default this to NO).
|
|
79
|
+
"COPY_PHASE_STRIP": "NO",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ios.icons.source:
|
|
83
|
+
# Designate the AppIcon set in Assets.xcassets as the app icon. Without
|
|
84
|
+
# this the catalog still compiles, but Xcode assigns no icon — the
|
|
85
|
+
# generated AppIcon.appiconset is silently ignored.
|
|
86
|
+
settings["ASSETCATALOG_COMPILER_APPICON_NAME"] = "AppIcon"
|
|
87
|
+
|
|
88
|
+
if ios.entitlements:
|
|
89
|
+
settings["CODE_SIGN_ENTITLEMENTS"] = f"{config.app_slug}.entitlements"
|
|
90
|
+
|
|
91
|
+
return settings
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _header_search_paths(layout: StagingLayout | None) -> str:
|
|
95
|
+
paths = ["$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"]
|
|
96
|
+
if layout is not None:
|
|
97
|
+
sdl_xc = layout.frameworks / "SDL3.xcframework"
|
|
98
|
+
if sdl_xc.is_dir():
|
|
99
|
+
for slice_name in ("ios-arm64_x86_64-simulator", "ios-arm64"):
|
|
100
|
+
paths.append(
|
|
101
|
+
f"$(PROJECT_DIR)/Frameworks/SDL3.xcframework/"
|
|
102
|
+
f"{slice_name}/SDL3.framework/Headers"
|
|
103
|
+
)
|
|
104
|
+
return " ".join(f'"{path}"' for path in paths)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def signing_settings(config: Config) -> dict[str, str]:
|
|
108
|
+
"""Map [tool.kivy.ios.signing] to CODE_SIGN_* build settings (spec 06)."""
|
|
109
|
+
ios = config.ios
|
|
110
|
+
assert ios is not None
|
|
111
|
+
signing = ios.signing
|
|
112
|
+
settings: dict[str, str] = {
|
|
113
|
+
"CODE_SIGN_STYLE": "Automatic" if signing.auto_signing else "Manual",
|
|
114
|
+
}
|
|
115
|
+
if signing.team_id:
|
|
116
|
+
settings["DEVELOPMENT_TEAM"] = signing.team_id
|
|
117
|
+
if signing.identity:
|
|
118
|
+
settings["CODE_SIGN_IDENTITY"] = signing.identity
|
|
119
|
+
if signing.provisioning_profile:
|
|
120
|
+
settings["PROVISIONING_PROFILE_SPECIFIER"] = signing.provisioning_profile
|
|
121
|
+
return settings
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def user_build_settings(config: Config) -> dict[str, str]:
|
|
125
|
+
"""Free-form [tool.kivy.ios.xcode.build_settings] (reserved keys already rejected)."""
|
|
126
|
+
if not config.ios:
|
|
127
|
+
return {}
|
|
128
|
+
return dict(config.ios.build_settings)
|