From d176e4eb8cc7551462790504a605b29c72b314cf Mon Sep 17 00:00:00 2001 From: leehack Date: Wed, 11 Nov 2026 04:40:33 -0500 Subject: [PATCH] Stop packaging the unused Apple LiteRT companion libraries from v0.19 v0.19 LiteRtLm embeds LiteRT and its GPU accelerator; dyld traces show the macOS libLiteRt and libLiteRtWebGpuAccelerator and the iOS loose libLiteRt are never opened (llamadart#1911). Packaging skips them, the release manifest drops their XCFramework zips, and a candidate that still ships them is rejected. --- README.md | 5 ++- docs/platform_strategy.md | 7 +++-- docs/v019_qualification.md | 1 + tools/litert_lm_symbols.py | 7 +++++ tools/package_upstream_prebuilts.py | 17 +++++++--- tools/test_package_upstream_prebuilts.py | 40 ++++++++++++++++++------ tools/test_v019_decisions.py | 24 +++++++++++++- tools/validate_release_manifest.py | 10 ++++-- tools/validate_runtime_artifacts.py | 17 ++++++++-- 9 files changed, 104 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 182fff5..cc17f1f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/platform_strategy.md b/docs/platform_strategy.md index 247ac2c..ea5540a 100644 --- a/docs/platform_strategy.md +++ b/docs/platform_strategy.md @@ -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. diff --git a/docs/v019_qualification.md b/docs/v019_qualification.md index ed60940..d5d3c7b 100644 --- a/docs/v019_qualification.md +++ b/docs/v019_qualification.md @@ -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 diff --git a/tools/litert_lm_symbols.py b/tools/litert_lm_symbols.py index 0f8a206..b609743 100644 --- a/tools/litert_lm_symbols.py +++ b/tools/litert_lm_symbols.py @@ -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 @@ -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): diff --git a/tools/package_upstream_prebuilts.py b/tools/package_upstream_prebuilts.py index b55aabf..9d343ad 100755 --- a/tools/package_upstream_prebuilts.py +++ b/tools/package_upstream_prebuilts.py @@ -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 ( @@ -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( diff --git a/tools/test_package_upstream_prebuilts.py b/tools/test_package_upstream_prebuilts.py index 3e505a1..2e659bf 100644 --- a/tools/test_package_upstream_prebuilts.py +++ b/tools/test_package_upstream_prebuilts.py @@ -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), \ @@ -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( diff --git a/tools/test_v019_decisions.py b/tools/test_v019_decisions.py index 384d84b..4e3de56 100644 --- a/tools/test_v019_decisions.py +++ b/tools/test_v019_decisions.py @@ -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, @@ -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", @@ -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")) diff --git a/tools/validate_release_manifest.py b/tools/validate_release_manifest.py index 9023ee3..fba3929 100644 --- a/tools/validate_release_manifest.py +++ b/tools/validate_release_manifest.py @@ -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, @@ -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", ] @@ -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" diff --git a/tools/validate_runtime_artifacts.py b/tools/validate_runtime_artifacts.py index 1b9b79a..8162a5d 100644 --- a/tools/validate_runtime_artifacts.py +++ b/tools/validate_runtime_artifacts.py @@ -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, @@ -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"), @@ -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: