Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,10 @@ v0.18 this includes the iOS `LiteRtMetalAccelerator` and
`LiteRtTopKMetalSampler` modules; their framework-relative loader paths avoid
flat dylibs that App Store bundles cannot ship. v0.19 `LiteRtLm` links its GPU
accelerator and sampler, so neither is needed; releases after `v0.19.1-2` ship
no Metal module.
no Metal module. Releases after `v0.19.1-3` also drop the macOS `libLiteRt` and
`libLiteRtWebGpuAccelerator` dylibs, the iOS loose `libLiteRt` and the matching
`LiteRt` and `LiteRtWebGpuAccelerator` XCFramework zips: v0.19 `LiteRtLm`
embeds LiteRT and never opens them.

The macOS packager embeds the primary framework's private dylib dependencies
inside `LiteRtLm.framework/Versions/A/Dependencies` and rewrites their loader
Expand Down
7 changes: 5 additions & 2 deletions docs/platform_strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,11 @@ sections, such as EmbeddingGemma-2. The build fails if a v0.19+ host on any
platform still imports `libLiteRt`, and those hosts report
`litert_lm_native_in_process_litert_abi_version()` 1, so consumers can refuse
external-weight bundles on older desktop runtimes. Bundles keep shipping the
upstream `libLiteRt` and `libLiteRtWebGpuAccelerator` files so the consumer
layout is unchanged, but the host loads neither.
upstream `libLiteRt` and `libLiteRtWebGpuAccelerator` files on Linux, Windows
and Android, but the host loads neither. Apple bundles after `v0.19.1-3` omit
the macOS `libLiteRt` and `libLiteRtWebGpuAccelerator` and the iOS loose
`libLiteRt` (`has_prebuilt_apple_litert_companions`), and so publish no
`LiteRt` or `LiteRtWebGpuAccelerator` XCFramework.

The rest of this section covers v0.16 to v0.18.

Expand Down
1 change: 1 addition & 0 deletions docs/v019_qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The exact-tag verification gate remains enabled.
| Protos | `Asr*` became `Speech2Text*` | Internal to the adapter. |
| Packaging | No `LiteRtTopK*Sampler` library; `libLiteRtLm` links Dawn | Validators and manifests key this on `has_prebuilt_topk_samplers`. |
| Apple GPU | `LiteRtLm` links its GPU accelerator: ml_drift Metal on iOS, WebGPU on macOS | LiteRT registers it before searching for a shared one, so the prebuilt Metal accelerator is never opened ([llamadart#1798](https://github.com/leehack/llamadart/issues/1798)). Packaging after `v0.19.1-2` skips it on iOS and macOS, requires the static accelerator symbol in `LiteRtLm`, and rejects a candidate that still ships it (`has_prebuilt_apple_metal_accelerator`). |
| Apple LiteRT companions | `LiteRtLm` embeds LiteRT and its GPU accelerator | dyld traces on macOS (CPU, GPU, vision, audio, MTP, embeddings, ASR) and an iOS simulator probe load neither `libLiteRt` nor `libLiteRtWebGpuAccelerator` ([llamadart#1911](https://github.com/leehack/llamadart/issues/1911)). Packaging after `v0.19.1-3` skips the macOS `libLiteRt` and `libLiteRtWebGpuAccelerator` and the iOS loose `libLiteRt`, and rejects a candidate that still ships them (`has_prebuilt_apple_litert_companions`). |

`ubuntu-latest` moved to 26.04, so a Linux x64 build there needs glibc 2.43 and
fails to load on Ubuntu 24.04. Linux x64 now builds on `ubuntu-24.04` and
Expand Down
7 changes: 7 additions & 0 deletions tools/litert_lm_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
]

APPLE_METAL_ACCELERATOR_LIBRARY = "libLiteRtMetalAccelerator.dylib"
APPLE_LITERT_LIBRARY = "libLiteRt.dylib"
APPLE_WEBGPU_ACCELERATOR_LIBRARY = "libLiteRtWebGpuAccelerator.dylib"

# LiteRT registers a statically linked GPU accelerator before it searches for a
# shared one (gpu_registry.cc), so these classes make the prebuilt Metal
Expand Down Expand Up @@ -161,6 +163,11 @@ def has_prebuilt_apple_metal_accelerator(upstream_tag: str) -> bool:
return not is_at_least(upstream_tag, (0, 19, 0))


def has_prebuilt_apple_litert_companions(upstream_tag: str) -> bool:
"""v0.19 LiteRtLm embeds LiteRT, so Apple never opens libLiteRt or the WebGPU accelerator."""
return not is_at_least(upstream_tag, (0, 19, 0))


def required_bridge_symbols(upstream_tag: str) -> list[bytes]:
symbols = list(BRIDGE_SYMBOLS)
if has_asr_bridge(upstream_tag):
Expand Down
17 changes: 12 additions & 5 deletions tools/package_upstream_prebuilts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from git_lfs_utils import materialize_git_lfs_libraries
from litert_lm_symbols import (
APPLE_METAL_ACCELERATOR_LIBRARY,
APPLE_LITERT_LIBRARY,
APPLE_WEBGPU_ACCELERATOR_LIBRARY,
has_prebuilt_apple_litert_companions,
has_prebuilt_apple_metal_accelerator,
)
from prebuilt_overrides import (
Expand Down Expand Up @@ -66,11 +69,15 @@ def extract_source(archive: Path, output_dir: Path) -> Path:


def unused_prebuilts(upstream_tag: str, platform: str) -> frozenset[str]:
if platform in {"ios", "macos"} and not has_prebuilt_apple_metal_accelerator(
upstream_tag
):
return frozenset({APPLE_METAL_ACCELERATOR_LIBRARY})
return frozenset()
unused: set[str] = set()
if platform in {"ios", "macos"}:
if not has_prebuilt_apple_metal_accelerator(upstream_tag):
unused.add(APPLE_METAL_ACCELERATOR_LIBRARY)
if not has_prebuilt_apple_litert_companions(upstream_tag):
unused.add(APPLE_LITERT_LIBRARY)
if platform == "macos":
unused.add(APPLE_WEBGPU_ACCELERATOR_LIBRARY)
return frozenset(unused)


def copy_prebuilts(
Expand Down
40 changes: 30 additions & 10 deletions tools/test_package_upstream_prebuilts.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,40 @@ def materialize(
b"\x7fELF materialized",
)

def test_skips_apple_metal_accelerator_from_v019(self) -> None:
def test_skips_unused_apple_libraries_from_v019(self) -> None:
targets = {
"macos_arm64": ("macos", "arm64"),
"ios_arm64": ("ios", "arm64"),
"ios_arm64_sim": ("ios", "arm64-sim"),
"android_arm64": ("android", "arm64"),
"linux_x86_64": ("linux", "x64"),
"windows_x86_64": ("windows", "x64"),
}
for tag, metal_copied in (("v0.18.1", True), ("v0.19.1", False)):
names = (
"libLiteRt.dylib",
"libLiteRtMetalAccelerator.dylib",
"libLiteRtWebGpuAccelerator.dylib",
"libLiteRtLm.dylib",
)
removed_v019 = {
("macos", "arm64"): {
"libLiteRt.dylib",
"libLiteRtMetalAccelerator.dylib",
"libLiteRtWebGpuAccelerator.dylib",
},
("ios", "arm64"): {"libLiteRt.dylib", "libLiteRtMetalAccelerator.dylib"},
("ios", "arm64-sim"): {
"libLiteRt.dylib",
"libLiteRtMetalAccelerator.dylib",
},
}
for tag in ("v0.18.1", "v0.19.1"):
with self.subTest(tag=tag), tempfile.TemporaryDirectory() as temp:
root = Path(temp)
for upstream_name in targets:
source_dir = root / "source" / "prebuilt" / upstream_name
source_dir.mkdir(parents=True)
for name in ("libLiteRt.dylib", "libLiteRtMetalAccelerator.dylib"):
for name in names:
(source_dir / name).write_bytes(b"binary")
output = root / "bin"
with patch.object(package_upstream_prebuilts, "BIN_DIR", output), \
Expand All @@ -89,13 +110,12 @@ def test_skips_apple_metal_accelerator_from_v019(self) -> None:
package_upstream_prebuilts.copy_prebuilts(
root / "source", upstream_tag=tag, clean=True)

for platform in ("macos", "ios", "android"):
staged = output / platform / "arm64"
self.assertTrue((staged / "libLiteRt.dylib").is_file())
self.assertEqual(
(staged / "libLiteRtMetalAccelerator.dylib").is_file(),
metal_copied or platform == "android",
)
for platform, arch in targets.values():
staged = {p.name for p in (output / platform / arch).iterdir()}
expected = set(names)
if tag == "v0.19.1":
expected -= removed_v019.get((platform, arch), set())
self.assertEqual(staged, expected, (platform, arch))

def test_overrides_only_does_not_download_or_copy_upstream_source(self) -> None:
with patch(
Expand Down
24 changes: 23 additions & 1 deletion tools/test_v019_decisions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ios_runtime_policy import verify_upstream_fst_gate
from litert_lm_symbols import (
APPLE_STATIC_GPU_ACCELERATOR_SYMBOLS,
has_prebuilt_apple_litert_companions,
has_prebuilt_apple_metal_accelerator,
has_prebuilt_topk_samplers,
required_bridge_symbols,
Expand Down Expand Up @@ -71,7 +72,7 @@ def test_apple_metal_accelerator_packaging_stops_at_v019(self) -> None:
self.assertFalse([p for p in required_runtime_artifacts("v0.19.1") if "Metal" in str(p)])
self.assertEqual(forbidden_runtime_artifacts("v0.18.1"), [])
self.assertEqual(
{p.as_posix() for p in forbidden_runtime_artifacts("v0.19.1")},
{p.as_posix() for p in forbidden_runtime_artifacts("v0.19.1") if "Metal" in p.name},
{
"bin/ios/arm64/LiteRtMetalAccelerator.framework/LiteRtMetalAccelerator",
"bin/ios/arm64-sim/LiteRtMetalAccelerator.framework/LiteRtMetalAccelerator",
Expand All @@ -81,6 +82,27 @@ def test_apple_metal_accelerator_packaging_stops_at_v019(self) -> None:
},
)

def test_apple_litert_companion_packaging_stops_at_v019(self) -> None:
litert_zips = {
"litert-lm-native-apple-LiteRt-xcframework-{tag}.zip",
"litert-lm-native-apple-LiteRtWebGpuAccelerator-xcframework-{tag}.zip",
}
self.assertTrue(has_prebuilt_apple_litert_companions("v0.18.1"))
self.assertFalse(has_prebuilt_apple_litert_companions("v0.19.1"))
self.assertTrue(litert_zips <= set(allowed_spm_assets("v0.18.1")))
self.assertFalse(litert_zips & set(allowed_spm_assets("v0.19.1")))
self.assertFalse(litert_zips & set(required_spm_assets("v0.19.1")))
self.assertEqual(forbidden_runtime_artifacts("v0.18.1"), [])
self.assertLessEqual(
{
"bin/ios/arm64/libLiteRt.dylib",
"bin/ios/arm64-sim/libLiteRt.dylib",
"bin/macos/arm64/libLiteRt.dylib",
"bin/macos/arm64/libLiteRtWebGpuAccelerator.dylib",
},
{p.as_posix() for p in forbidden_runtime_artifacts("v0.19.1")},
)

def test_v019_apple_runtimes_require_a_static_gpu_accelerator(self) -> None:
required = (required_c_api_symbols("v0.19.1")
+ required_bridge_symbols("v0.19.1"))
Expand Down
10 changes: 8 additions & 2 deletions tools/validate_release_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from fetch_litert_lm_asr_smoke_assets import ASSETS
from litert_lm_symbols import (
has_asr_bridge,
has_prebuilt_apple_litert_companions,
has_prebuilt_apple_metal_accelerator,
has_prebuilt_topk_samplers,
is_at_least,
Expand Down Expand Up @@ -54,10 +55,13 @@

# The Apple packager emits these when their pinned v0.16 runtime inputs exist.
# Keep the optional inventory explicit so arbitrary manifest-added zips still fail.
V0_16_ALLOWED_SPM_COMPANION_XCFRAMEWORKS = [
"litert-lm-native-apple-GemmaModelConstraintProvider-xcframework-{tag}.zip",
APPLE_LITERT_COMPANION_SPM_XCFRAMEWORKS = [
"litert-lm-native-apple-LiteRt-xcframework-{tag}.zip",
"litert-lm-native-apple-LiteRtWebGpuAccelerator-xcframework-{tag}.zip",
]

V0_16_ALLOWED_SPM_COMPANION_XCFRAMEWORKS = [
"litert-lm-native-apple-GemmaModelConstraintProvider-xcframework-{tag}.zip",
"litert-lm-native-apple-WebgpuDawn-xcframework-{tag}.zip",
]

Expand Down Expand Up @@ -634,6 +638,8 @@ def allowed_spm_assets(compatibility_tag: str) -> list[str]:
assets = required_spm_assets(compatibility_tag)
if is_at_least(compatibility_tag, (0, 16, 0)):
assets.extend(V0_16_ALLOWED_SPM_COMPANION_XCFRAMEWORKS)
if has_prebuilt_apple_litert_companions(compatibility_tag):
assets.extend(APPLE_LITERT_COMPANION_SPM_XCFRAMEWORKS)
if has_prebuilt_topk_samplers(compatibility_tag):
assets.append(
"litert-lm-native-apple-LiteRtTopKWebGpuSampler-xcframework-{tag}.zip"
Expand Down
17 changes: 14 additions & 3 deletions tools/validate_runtime_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pathlib import Path

from litert_lm_symbols import (
has_prebuilt_apple_litert_companions,
has_prebuilt_apple_metal_accelerator,
has_prebuilt_topk_samplers,
is_at_least,
Expand Down Expand Up @@ -38,6 +39,13 @@
Path("bin/macos/arm64/libLiteRtMetalAccelerator.dylib"),
]

APPLE_LITERT_COMPANION_ARTIFACTS = [
Path("bin/ios/arm64/libLiteRt.dylib"),
Path("bin/ios/arm64-sim/libLiteRt.dylib"),
Path("bin/macos/arm64/libLiteRt.dylib"),
Path("bin/macos/arm64/libLiteRtWebGpuAccelerator.dylib"),
]

PREBUILT_SAMPLER_IOS_ARTIFACTS = [
Path("bin/ios/arm64/LiteRtTopKMetalSampler.framework/LiteRtTopKMetalSampler"),
Path("bin/ios/arm64-sim/LiteRtTopKMetalSampler.framework/LiteRtTopKMetalSampler"),
Expand Down Expand Up @@ -69,9 +77,12 @@ def required_runtime_artifacts(


def forbidden_runtime_artifacts(upstream_tag: str) -> list[Path]:
if has_prebuilt_apple_metal_accelerator(upstream_tag):
return []
return list(APPLE_METAL_ACCELERATOR_ARTIFACTS)
forbidden: list[Path] = []
if not has_prebuilt_apple_metal_accelerator(upstream_tag):
forbidden.extend(APPLE_METAL_ACCELERATOR_ARTIFACTS)
if not has_prebuilt_apple_litert_companions(upstream_tag):
forbidden.extend(APPLE_LITERT_COMPANION_ARTIFACTS)
return forbidden


def main() -> int:
Expand Down
Loading