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
kivyforge/cli/lock.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""``kivyforge lock`` — resolve dependencies into pylock.<platform>.toml.
|
|
2
|
+
|
|
3
|
+
The verb resolves the target platform (``-p`` / ``KIVYFORGE_PLATFORM`` / host)
|
|
4
|
+
and dispatches to that backend's lock engine, writing ``pylock.<platform>.toml``.
|
|
5
|
+
Each backend supplies its own build/serialize/compare callables; the drift check
|
|
6
|
+
(``pyproject_sha256``) and atomic write are shared.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
|
|
19
|
+
from ..config import ConfigError, load_config
|
|
20
|
+
from ..lock.reader import LockError, is_in_sync
|
|
21
|
+
from ..platforms.base import HostCapabilityError
|
|
22
|
+
from ..platforms.ios.lock import (
|
|
23
|
+
BuildError,
|
|
24
|
+
build_lockfile,
|
|
25
|
+
diff_summary,
|
|
26
|
+
dumps,
|
|
27
|
+
load,
|
|
28
|
+
semantic_equal,
|
|
29
|
+
)
|
|
30
|
+
from ._common import ToolchainError, lockfile_path_for
|
|
31
|
+
from ._platform import platform_option, resolve_target
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class _LockOps:
|
|
36
|
+
"""Per-platform lock callables (raise ``BuildError``/``LockError``)."""
|
|
37
|
+
|
|
38
|
+
build: Callable
|
|
39
|
+
dumps: Callable
|
|
40
|
+
load: Callable
|
|
41
|
+
semantic_equal: Callable
|
|
42
|
+
diff_summary: Callable
|
|
43
|
+
build_error: type[Exception]
|
|
44
|
+
require_ios: bool
|
|
45
|
+
require_macos: bool
|
|
46
|
+
require_linux: bool = False
|
|
47
|
+
require_windows: bool = False
|
|
48
|
+
require_android: bool = False
|
|
49
|
+
# Wheel+runtime backends (macOS/Linux) surface non-fatal lock warnings
|
|
50
|
+
# (e.g. accepting a vendored plain linux_* wheel) via an on_warning callback.
|
|
51
|
+
emits_warnings: bool = False
|
|
52
|
+
# Most backends resolve with pip alone, so `lock` runs anywhere and the
|
|
53
|
+
# result is committed. iOS is the exception: resolving declared Swift
|
|
54
|
+
# packages shells out to `swift package resolve`, which needs the Xcode
|
|
55
|
+
# toolchain, so the whole iOS workflow — lock included — is macOS-only.
|
|
56
|
+
requires_host_toolchain: bool = False
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _lock_ops(platform: str) -> _LockOps:
|
|
60
|
+
if platform == "ios":
|
|
61
|
+
# Reference the module globals (not a local import) so tests can patch
|
|
62
|
+
# ``lock_cli.build_lockfile`` with a fake-injecting wrapper.
|
|
63
|
+
return _LockOps(
|
|
64
|
+
build=build_lockfile,
|
|
65
|
+
dumps=dumps,
|
|
66
|
+
load=load,
|
|
67
|
+
semantic_equal=semantic_equal,
|
|
68
|
+
diff_summary=diff_summary,
|
|
69
|
+
build_error=BuildError,
|
|
70
|
+
require_ios=True,
|
|
71
|
+
require_macos=False,
|
|
72
|
+
requires_host_toolchain=True,
|
|
73
|
+
)
|
|
74
|
+
if platform == "macos":
|
|
75
|
+
from ..platforms.macos import lock as macos_lock
|
|
76
|
+
|
|
77
|
+
return _LockOps(
|
|
78
|
+
build=macos_lock.build_macos_lockfile,
|
|
79
|
+
dumps=macos_lock.dumps,
|
|
80
|
+
load=macos_lock.load,
|
|
81
|
+
semantic_equal=macos_lock.semantic_equal,
|
|
82
|
+
diff_summary=macos_lock.diff_summary,
|
|
83
|
+
build_error=macos_lock.MacosBuildError,
|
|
84
|
+
require_ios=False,
|
|
85
|
+
require_macos=True,
|
|
86
|
+
emits_warnings=True,
|
|
87
|
+
)
|
|
88
|
+
if platform == "linux":
|
|
89
|
+
from ..platforms.linux import lock as linux_lock
|
|
90
|
+
|
|
91
|
+
return _LockOps(
|
|
92
|
+
build=linux_lock.build_linux_lockfile,
|
|
93
|
+
dumps=linux_lock.dumps,
|
|
94
|
+
load=linux_lock.load,
|
|
95
|
+
semantic_equal=linux_lock.semantic_equal,
|
|
96
|
+
diff_summary=linux_lock.diff_summary,
|
|
97
|
+
build_error=linux_lock.LinuxBuildError,
|
|
98
|
+
require_ios=False,
|
|
99
|
+
require_macos=False,
|
|
100
|
+
require_linux=True,
|
|
101
|
+
emits_warnings=True,
|
|
102
|
+
)
|
|
103
|
+
if platform == "windows":
|
|
104
|
+
from ..platforms.windows import lock as windows_lock
|
|
105
|
+
|
|
106
|
+
return _LockOps(
|
|
107
|
+
build=windows_lock.build_windows_lockfile,
|
|
108
|
+
dumps=windows_lock.dumps,
|
|
109
|
+
load=windows_lock.load,
|
|
110
|
+
semantic_equal=windows_lock.semantic_equal,
|
|
111
|
+
diff_summary=windows_lock.diff_summary,
|
|
112
|
+
build_error=windows_lock.WindowsBuildError,
|
|
113
|
+
require_ios=False,
|
|
114
|
+
require_macos=False,
|
|
115
|
+
require_windows=True,
|
|
116
|
+
emits_warnings=True,
|
|
117
|
+
)
|
|
118
|
+
if platform == "android":
|
|
119
|
+
from ..platforms.android.lock import builder as android_builder
|
|
120
|
+
from ..platforms.android.lock import reader as android_reader
|
|
121
|
+
from ..platforms.android.lock import writer as android_writer
|
|
122
|
+
|
|
123
|
+
return _LockOps(
|
|
124
|
+
build=android_builder.build_lockfile,
|
|
125
|
+
dumps=android_writer.dumps,
|
|
126
|
+
load=android_reader.load,
|
|
127
|
+
semantic_equal=android_builder.semantic_equal,
|
|
128
|
+
diff_summary=android_builder.diff_summary,
|
|
129
|
+
build_error=android_builder.BuildError,
|
|
130
|
+
require_ios=False,
|
|
131
|
+
require_macos=False,
|
|
132
|
+
require_android=True,
|
|
133
|
+
)
|
|
134
|
+
raise ToolchainError(
|
|
135
|
+
f"`kivyforge lock` does not support platform {platform!r} yet."
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _require_host_toolchain(backend) -> None:
|
|
140
|
+
"""Gate `lock` for backends whose resolution needs a host toolchain."""
|
|
141
|
+
try:
|
|
142
|
+
backend.check_host_capability()
|
|
143
|
+
except HostCapabilityError as exc:
|
|
144
|
+
raise ToolchainError(str(exc)) from exc
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@click.command()
|
|
148
|
+
@platform_option
|
|
149
|
+
@click.option("--update", is_flag=True, help="Re-resolve even if the lock is in sync.")
|
|
150
|
+
@click.option("--offline", is_flag=True, help="Use cached resolution results only.")
|
|
151
|
+
@click.option(
|
|
152
|
+
"--check",
|
|
153
|
+
is_flag=True,
|
|
154
|
+
help="CI pre-flight: exit non-zero if the lock is stale; write nothing.",
|
|
155
|
+
)
|
|
156
|
+
def lock(cli_platform: str | None, update: bool, offline: bool, check: bool) -> None:
|
|
157
|
+
"""Generate pylock.<platform>.toml from pyproject.toml."""
|
|
158
|
+
backend, project_root = resolve_target(cli_platform, verb="lock")
|
|
159
|
+
ops = _lock_ops(backend.name)
|
|
160
|
+
if ops.requires_host_toolchain:
|
|
161
|
+
_require_host_toolchain(backend)
|
|
162
|
+
|
|
163
|
+
pyproject = project_root / "pyproject.toml"
|
|
164
|
+
pyproject_text = pyproject.read_text(encoding="utf-8")
|
|
165
|
+
out_path = lockfile_path_for(backend.name, project_root)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
config = load_config(
|
|
169
|
+
pyproject,
|
|
170
|
+
require_ios=ops.require_ios,
|
|
171
|
+
require_macos=ops.require_macos,
|
|
172
|
+
require_linux=ops.require_linux,
|
|
173
|
+
require_windows=ops.require_windows,
|
|
174
|
+
require_android=ops.require_android,
|
|
175
|
+
)
|
|
176
|
+
except ConfigError as exc:
|
|
177
|
+
raise ToolchainError(exc.format()) from exc
|
|
178
|
+
|
|
179
|
+
if check:
|
|
180
|
+
_run_check(ops, config, pyproject_text, out_path, project_root, offline)
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
if out_path.is_file() and not update:
|
|
184
|
+
try:
|
|
185
|
+
existing = ops.load(out_path)
|
|
186
|
+
except LockError:
|
|
187
|
+
existing = None
|
|
188
|
+
if existing is not None and is_in_sync(existing, pyproject_text):
|
|
189
|
+
click.echo(f"{out_path.name} is already in sync with pyproject.toml.")
|
|
190
|
+
click.echo(" (use --update to force re-resolution)")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
new_lock = _build(ops, config, pyproject_text, project_root, offline)
|
|
194
|
+
_atomic_write(out_path, ops.dumps(new_lock))
|
|
195
|
+
click.echo(f"Wrote {out_path.name} ({len(new_lock.packages)} packages pinned).")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _run_check(
|
|
199
|
+
ops: _LockOps,
|
|
200
|
+
config,
|
|
201
|
+
pyproject_text: str,
|
|
202
|
+
out_path: Path,
|
|
203
|
+
project_root: Path,
|
|
204
|
+
offline: bool,
|
|
205
|
+
) -> None:
|
|
206
|
+
if not out_path.is_file():
|
|
207
|
+
raise ToolchainError(
|
|
208
|
+
f"{out_path.name} does not exist. Run `kivyforge lock` first."
|
|
209
|
+
)
|
|
210
|
+
try:
|
|
211
|
+
existing = ops.load(out_path)
|
|
212
|
+
except LockError as exc:
|
|
213
|
+
raise ToolchainError(str(exc)) from exc
|
|
214
|
+
|
|
215
|
+
candidate = _build(ops, config, pyproject_text, project_root, offline)
|
|
216
|
+
if ops.semantic_equal(existing, candidate):
|
|
217
|
+
click.echo(f"{out_path.name} is up to date.")
|
|
218
|
+
return
|
|
219
|
+
click.echo(f"{out_path.name} is out of date:", err=True)
|
|
220
|
+
for line in ops.diff_summary(existing, candidate):
|
|
221
|
+
click.echo(line, err=True)
|
|
222
|
+
raise ToolchainError("lock is stale; run `kivyforge lock`.")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _build(
|
|
226
|
+
ops: _LockOps, config, pyproject_text: str, project_root: Path, offline: bool
|
|
227
|
+
):
|
|
228
|
+
kwargs = {}
|
|
229
|
+
if ops.emits_warnings:
|
|
230
|
+
kwargs["on_warning"] = lambda msg: click.echo(msg, err=True)
|
|
231
|
+
try:
|
|
232
|
+
return ops.build(
|
|
233
|
+
config,
|
|
234
|
+
pyproject_text,
|
|
235
|
+
project_root=project_root,
|
|
236
|
+
offline=offline,
|
|
237
|
+
**kwargs,
|
|
238
|
+
)
|
|
239
|
+
except ops.build_error as exc:
|
|
240
|
+
raise ToolchainError(str(exc)) from exc
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _atomic_write(path: Path, text: str) -> None:
|
|
244
|
+
"""Write to a tempfile, fsync, then rename (spec 02 step 5).
|
|
245
|
+
|
|
246
|
+
Always LF: a lock file is committed and compared across machines, so
|
|
247
|
+
letting the host's default line ending through would make a re-lock on
|
|
248
|
+
another OS rewrite every line. The lock must depend on the target, never
|
|
249
|
+
on who ran it.
|
|
250
|
+
"""
|
|
251
|
+
directory = path.parent
|
|
252
|
+
fd, tmp = tempfile.mkstemp(dir=directory, prefix=".pylock.", suffix=".tmp")
|
|
253
|
+
try:
|
|
254
|
+
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as f:
|
|
255
|
+
f.write(text)
|
|
256
|
+
f.flush()
|
|
257
|
+
os.fsync(f.fileno())
|
|
258
|
+
os.replace(tmp, path)
|
|
259
|
+
finally:
|
|
260
|
+
if os.path.exists(tmp):
|
|
261
|
+
os.unlink(tmp)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""``kivyforge open`` — open the generated Xcode project (spec 05)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ._platform import platform_option, resolve_target
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command(name="open")
|
|
11
|
+
@platform_option
|
|
12
|
+
def open_(cli_platform: str | None) -> None:
|
|
13
|
+
"""Open <app>-ios/<app>.xcodeproj in Xcode."""
|
|
14
|
+
backend, project_root = resolve_target(cli_platform, verb="open")
|
|
15
|
+
backend.open_project(project_root)
|
kivyforge/cli/package.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""``kivyforge package`` — build the distributable, signed artifact (docs 02/06).
|
|
2
|
+
|
|
3
|
+
``package`` produces the finished, signed runnable artifact for the resolved
|
|
4
|
+
target (wrapping it into an installer/container is out of scope). For iOS this is
|
|
5
|
+
the release archive + exported ``.ipa``; other platforms register their own
|
|
6
|
+
shapes via ``Platform.package_formats`` and are wired in as they land.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from ._common import ToolchainError
|
|
14
|
+
from ._platform import platform_option, reject_android_only, resolve_target
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command()
|
|
18
|
+
@platform_option
|
|
19
|
+
@click.option(
|
|
20
|
+
"-f",
|
|
21
|
+
"--format",
|
|
22
|
+
"fmt",
|
|
23
|
+
default=None,
|
|
24
|
+
help="Artifact format for the target (defaults to the platform's default).",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--arch",
|
|
28
|
+
# x86_64 stays: it is the Linux arch and an Android ABI, not a macOS one.
|
|
29
|
+
type=click.Choice(["arm64", "x86_64"]),
|
|
30
|
+
default=None,
|
|
31
|
+
help="macOS: assemble a subset of the locked archs.",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--team-id", default=None, help="Override [tool.kivy.ios.signing].team_id."
|
|
35
|
+
)
|
|
36
|
+
@click.option(
|
|
37
|
+
"--signing-identity",
|
|
38
|
+
default=None,
|
|
39
|
+
help="Override [tool.kivy.<platform>.signing].identity.",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--export-method",
|
|
43
|
+
type=click.Choice(["app-store", "ad-hoc", "development"]),
|
|
44
|
+
default="app-store",
|
|
45
|
+
help="iOS export method.",
|
|
46
|
+
)
|
|
47
|
+
@click.option(
|
|
48
|
+
"--notarize/--no-notarize",
|
|
49
|
+
"notarize",
|
|
50
|
+
default=None,
|
|
51
|
+
help="macOS: notarize + staple the signed .app (default: when "
|
|
52
|
+
"[tool.kivy.macos.signing].notary_profile is configured).",
|
|
53
|
+
)
|
|
54
|
+
@click.option(
|
|
55
|
+
"--notary-profile",
|
|
56
|
+
default=None,
|
|
57
|
+
help="macOS: override [tool.kivy.macos.signing].notary_profile.",
|
|
58
|
+
)
|
|
59
|
+
@click.option(
|
|
60
|
+
"--no-verify-lock", is_flag=True, help="Skip the pyproject drift check (CI only)."
|
|
61
|
+
)
|
|
62
|
+
@click.option("--no-cache", is_flag=True, help="Force re-download of every artifact.")
|
|
63
|
+
# --- Android-only ---
|
|
64
|
+
@click.option(
|
|
65
|
+
"--abi",
|
|
66
|
+
type=click.Choice(["arm64_v8a", "x86_64"]),
|
|
67
|
+
default=None,
|
|
68
|
+
help="Android: restrict this package to one ABI.",
|
|
69
|
+
)
|
|
70
|
+
@click.option(
|
|
71
|
+
"--keystore",
|
|
72
|
+
default=None,
|
|
73
|
+
help="Android: override [tool.kivy.android.signing].keystore.",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--key-alias",
|
|
77
|
+
default=None,
|
|
78
|
+
help="Android: override [tool.kivy.android.signing].key_alias.",
|
|
79
|
+
)
|
|
80
|
+
def package(
|
|
81
|
+
cli_platform: str | None,
|
|
82
|
+
fmt: str | None,
|
|
83
|
+
arch: str | None,
|
|
84
|
+
team_id: str | None,
|
|
85
|
+
signing_identity: str | None,
|
|
86
|
+
export_method: str,
|
|
87
|
+
notarize: bool | None,
|
|
88
|
+
notary_profile: str | None,
|
|
89
|
+
no_verify_lock: bool,
|
|
90
|
+
no_cache: bool,
|
|
91
|
+
abi: str | None,
|
|
92
|
+
keystore: str | None,
|
|
93
|
+
key_alias: str | None,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Build the signed, distributable artifact for the resolved platform."""
|
|
96
|
+
backend, project_root = resolve_target(cli_platform, verb="package")
|
|
97
|
+
reject_android_only(
|
|
98
|
+
backend,
|
|
99
|
+
{"--abi": abi, "--keystore": keystore, "--key-alias": key_alias},
|
|
100
|
+
)
|
|
101
|
+
fmt = _resolve_format(backend, fmt)
|
|
102
|
+
|
|
103
|
+
backend.package(
|
|
104
|
+
project_root,
|
|
105
|
+
fmt=fmt,
|
|
106
|
+
arch=arch,
|
|
107
|
+
team_id=team_id,
|
|
108
|
+
signing_identity=signing_identity,
|
|
109
|
+
export_method=export_method,
|
|
110
|
+
notarize=notarize,
|
|
111
|
+
notary_profile=notary_profile,
|
|
112
|
+
no_verify_lock=no_verify_lock,
|
|
113
|
+
no_cache=no_cache,
|
|
114
|
+
abi=abi,
|
|
115
|
+
keystore=keystore,
|
|
116
|
+
key_alias=key_alias,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _resolve_format(backend, fmt: str | None) -> str:
|
|
121
|
+
"""Validate/resolve the ``-f`` format against the backend's advertised set."""
|
|
122
|
+
if fmt is None:
|
|
123
|
+
default = backend.default_package_format
|
|
124
|
+
if default is None:
|
|
125
|
+
raise ToolchainError(
|
|
126
|
+
f"{backend.name!r} does not define a package format yet."
|
|
127
|
+
)
|
|
128
|
+
return default
|
|
129
|
+
if fmt not in backend.package_formats:
|
|
130
|
+
allowed = ", ".join(backend.package_formats) or "none"
|
|
131
|
+
raise ToolchainError(
|
|
132
|
+
f"unknown package format {fmt!r} for {backend.name!r}; allowed: {allowed}."
|
|
133
|
+
)
|
|
134
|
+
return fmt
|
kivyforge/cli/run.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""``kivyforge run`` — build, install, and launch on a simulator/device (spec 05)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from click.core import ParameterSource
|
|
7
|
+
|
|
8
|
+
from ..platforms.ios.cli import ios_list_devices
|
|
9
|
+
from ._common import ToolchainError
|
|
10
|
+
from ._platform import platform_option, reject_android_only, resolve_target
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.command()
|
|
14
|
+
@platform_option
|
|
15
|
+
@click.option(
|
|
16
|
+
"--simulator",
|
|
17
|
+
"target",
|
|
18
|
+
flag_value="simulator",
|
|
19
|
+
default=True,
|
|
20
|
+
help="Target the iOS simulator (default).",
|
|
21
|
+
)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--device",
|
|
24
|
+
"target",
|
|
25
|
+
flag_value="device",
|
|
26
|
+
help="Target a connected device (Android: a physical one, never an AVD).",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--arch",
|
|
30
|
+
# x86_64 stays: it is the Linux arch and an Android ABI, not a macOS one.
|
|
31
|
+
type=click.Choice(["arm64", "x86_64", "arm64_v8a"]),
|
|
32
|
+
default=None,
|
|
33
|
+
help="macOS: a subset of locked archs; Android: restrict to one ABI.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--destination", default=None, help="Specific simulator/device by name or UDID."
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--list-devices",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
help="Print available simulators and devices, then exit.",
|
|
42
|
+
)
|
|
43
|
+
@click.option(
|
|
44
|
+
"--no-build",
|
|
45
|
+
is_flag=True,
|
|
46
|
+
help="Skip the implicit build; install + launch the existing app.",
|
|
47
|
+
)
|
|
48
|
+
# --- Android-only ---
|
|
49
|
+
@click.option(
|
|
50
|
+
"--emulator", is_flag=True, help="Android: boot/target an emulator (AVD)."
|
|
51
|
+
)
|
|
52
|
+
@click.option("--avd", default=None, help="Android: which AVD to boot (--emulator).")
|
|
53
|
+
@click.option(
|
|
54
|
+
"--serial", default=None, help="Android: which adb device when several attach."
|
|
55
|
+
)
|
|
56
|
+
@click.option(
|
|
57
|
+
"--smoke",
|
|
58
|
+
is_flag=True,
|
|
59
|
+
help="Android: run the generated contract smoke test instead of launching.",
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--release",
|
|
63
|
+
is_flag=True,
|
|
64
|
+
help="Android (with --smoke): target the release variant.",
|
|
65
|
+
)
|
|
66
|
+
@click.option(
|
|
67
|
+
"--abi",
|
|
68
|
+
type=click.Choice(["arm64_v8a", "x86_64"]),
|
|
69
|
+
default=None,
|
|
70
|
+
help="Android: restrict the implicit build to one ABI.",
|
|
71
|
+
)
|
|
72
|
+
def run(
|
|
73
|
+
cli_platform: str | None,
|
|
74
|
+
target: str,
|
|
75
|
+
arch: str | None,
|
|
76
|
+
destination: str | None,
|
|
77
|
+
list_devices: bool,
|
|
78
|
+
no_build: bool,
|
|
79
|
+
emulator: bool,
|
|
80
|
+
avd: str | None,
|
|
81
|
+
serial: str | None,
|
|
82
|
+
smoke: bool,
|
|
83
|
+
release: bool,
|
|
84
|
+
abi: str | None,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Build (unless --no-build), install, and launch the app."""
|
|
87
|
+
backend, project_root = resolve_target(cli_platform, verb="run")
|
|
88
|
+
|
|
89
|
+
reject_android_only(
|
|
90
|
+
backend,
|
|
91
|
+
{
|
|
92
|
+
"--emulator": emulator,
|
|
93
|
+
"--avd": avd,
|
|
94
|
+
"--serial": serial,
|
|
95
|
+
"--smoke": smoke,
|
|
96
|
+
"--release": release,
|
|
97
|
+
"--abi": abi,
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
abi = abi or arch
|
|
101
|
+
|
|
102
|
+
if backend.name == "android":
|
|
103
|
+
from ..platforms.android.cli import android_run, android_smoke
|
|
104
|
+
|
|
105
|
+
# --simulator is the default flag value, so only an *explicit* one is a
|
|
106
|
+
# mistake worth reporting; the source is how click tells the two apart.
|
|
107
|
+
source = click.get_current_context().get_parameter_source("target")
|
|
108
|
+
explicit = target if source is ParameterSource.COMMANDLINE else None
|
|
109
|
+
if explicit == "simulator":
|
|
110
|
+
raise ToolchainError(
|
|
111
|
+
"--simulator is iOS-only; Android's equivalent is --emulator."
|
|
112
|
+
)
|
|
113
|
+
if explicit == "device" and emulator:
|
|
114
|
+
raise ToolchainError(
|
|
115
|
+
"--device and --emulator select opposite targets; pass one, or "
|
|
116
|
+
"neither to auto-select (a single attached device, else an AVD)."
|
|
117
|
+
)
|
|
118
|
+
if explicit == "device" and avd is not None:
|
|
119
|
+
raise ToolchainError(
|
|
120
|
+
"--avd names an emulator to boot, which --device rules out; pass one."
|
|
121
|
+
)
|
|
122
|
+
require_physical = explicit == "device"
|
|
123
|
+
|
|
124
|
+
if list_devices:
|
|
125
|
+
from ..platforms.android import adb as adb_mod
|
|
126
|
+
|
|
127
|
+
click.echo("Devices: " + (", ".join(adb_mod.connected_devices()) or "none"))
|
|
128
|
+
click.echo("AVDs: " + (", ".join(adb_mod.available_avds()) or "none"))
|
|
129
|
+
return
|
|
130
|
+
if smoke:
|
|
131
|
+
android_smoke(
|
|
132
|
+
project_root,
|
|
133
|
+
release=release,
|
|
134
|
+
abi=abi,
|
|
135
|
+
serial=serial,
|
|
136
|
+
avd=avd,
|
|
137
|
+
prefer_emulator=emulator or avd is not None,
|
|
138
|
+
require_physical=require_physical,
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
android_run(
|
|
142
|
+
project_root,
|
|
143
|
+
no_build=no_build,
|
|
144
|
+
abi=abi,
|
|
145
|
+
serial=serial or destination,
|
|
146
|
+
avd=avd,
|
|
147
|
+
prefer_emulator=emulator or avd is not None,
|
|
148
|
+
require_physical=require_physical,
|
|
149
|
+
)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
if list_devices:
|
|
153
|
+
ios_list_devices()
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
backend.run(
|
|
157
|
+
project_root,
|
|
158
|
+
target=target,
|
|
159
|
+
arch=arch,
|
|
160
|
+
destination=destination,
|
|
161
|
+
no_build=no_build,
|
|
162
|
+
)
|
kivyforge/cli/status.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""``kivyforge status`` — read-only project state snapshot (spec 05).
|
|
2
|
+
|
|
3
|
+
Platform-aware: resolves the target (``-p`` / ``KIVYFORGE_PLATFORM`` / host) and
|
|
4
|
+
dispatches to that backend's ``status`` over its ``pyproject.toml`` +
|
|
5
|
+
``pylock.<platform>.toml``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ._platform import platform_option, resolve_target
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command()
|
|
16
|
+
@platform_option
|
|
17
|
+
def status(cli_platform: str | None) -> None:
|
|
18
|
+
"""Show app identity, Python version, lock sync, and build state."""
|
|
19
|
+
backend, project_root = resolve_target(cli_platform, verb="status")
|
|
20
|
+
backend.status(project_root)
|