Kit Manager: detect unsupported kits via platform database

Scan sedona/platforms/db/ to find which native kits are compiled for
the SAX's platform.  Kits with native code for other platforms, kits
providing a PlatformService for a different device, and their
transitive dependents are placed in a "Not supported" section with a
red-cross checkbox that cannot be toggled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-11 14:21:00 +03:00
co-authored by Claude Opus 4.6
parent f1cc15f70f
commit 48dfd82832
3 changed files with 179 additions and 6 deletions
+153
View File
@@ -246,9 +246,162 @@ def list_component_types(kit_name):
return sorted(entries)
_unsupported_cache = {} # frozenset(sax kit names) -> set of unsupported kit names
def _platforms_db():
return os.path.join(app_state.SEDONA_HOME, "platforms", "db")
def _platform_native_kits():
"""Return (matching, all_platforms) — two sets of kit names.
*matching* is the union of nativeKit names from platform builds that include
every PlatformService-providing kit the SAX declares. *all_platforms* is the
union across every build in the database regardless of match.
A kit that appears in *all_platforms* but not in *matching* has native code
compiled for a different device and cannot work with this SAX.
"""
sax_names = set(app_state.active_schema_kits.keys())
db = _platforms_db()
if not os.path.isdir(db):
return None, None
sax_platform_kits = set()
for name in sax_names:
kit_dir = os.path.join(_manifests_dir(), name)
if not os.path.isdir(kit_dir):
continue
for entry in os.listdir(kit_dir):
if not (entry.startswith(f"{name}-") and entry.endswith(".xml")):
continue
try:
tree = ET.parse(os.path.join(kit_dir, entry))
except ET.ParseError:
continue
for t in tree.getroot().findall(".//type"):
text = t.get("name", "") + " " + t.get("base", "")
if "PlatformService" in text:
sax_platform_kits.add(name)
break
if not sax_platform_kits:
return None, None
matching = set()
all_platforms = set()
found = False
for vendor in os.listdir(db):
vd = os.path.join(db, vendor)
if not os.path.isdir(vd):
continue
for device in os.listdir(vd):
dd = os.path.join(vd, device)
if not os.path.isdir(dd):
continue
for build in os.listdir(dd):
mf = os.path.join(dd, build, ".par", "platformManifest.xml")
if not os.path.isfile(mf):
continue
try:
tree = ET.parse(mf)
except ET.ParseError:
continue
nk_names = set()
for nk in tree.getroot().findall(".//nativeKit"):
nk_names.add(nk.get("depend", "").split()[0])
all_platforms.update(nk_names)
if sax_platform_kits <= nk_names:
matching.update(nk_names)
found = True
return (matching if found else None), all_platforms
def unsupported_kits():
"""Kit names that cannot work with the open file's platform.
A kit is unsupported when:
- it has native code (hasNatives or listed as nativeKit on some platform)
but is not compiled into any build matching the SAX's device, or
- it provides a PlatformService type for a different device.
Kits that depend on an unsupported kit are also flagged.
"""
sax_names = frozenset(app_state.active_schema_kits.keys())
if sax_names in _unsupported_cache:
return _unsupported_cache[sax_names]
matching_natives, all_natives = _platform_native_kits()
if matching_natives is None:
_unsupported_cache[sax_names] = set()
return set()
unsupported = set()
all_kits = list_kits()
for name in all_kits:
if name in sax_names:
continue
# Native on another platform but not on the matching one.
if name in all_natives and name not in matching_natives:
unsupported.add(name)
continue
kit_dir = os.path.join(_manifests_dir(), name)
if not os.path.isdir(kit_dir):
continue
for entry in os.listdir(kit_dir):
if not (entry.startswith(f"{name}-") and entry.endswith(".xml")):
continue
try:
tree = ET.parse(os.path.join(kit_dir, entry))
except ET.ParseError:
continue
root = tree.getroot()
has_natives = root.get("hasNatives") == "true"
has_platform_type = any(
"PlatformService" in (t.get("name", "") + " " + t.get("base", ""))
for t in root.findall(".//type")
)
if (has_natives or has_platform_type) and name not in matching_natives:
unsupported.add(name)
break
# Transitive: kits that depend on an unsupported kit
changed = True
while changed:
changed = False
for name in all_kits:
if name in unsupported or name in sax_names:
continue
kit_dir = os.path.join(_manifests_dir(), name)
if not os.path.isdir(kit_dir):
continue
for entry in os.listdir(kit_dir):
if not (entry.startswith(f"{name}-") and entry.endswith(".xml")):
continue
try:
tree = ET.parse(os.path.join(kit_dir, entry))
except ET.ParseError:
continue
for dep in tree.getroot().findall(".//depend"):
dep_name = dep.get("on", "").split()[0]
if dep_name in unsupported:
unsupported.add(name)
changed = True
break
_unsupported_cache[sax_names] = unsupported
return unsupported
def invalidate():
"""Clears every cache, for when Sedona home changes or a kit is repinned."""
_versions_cache.clear()
_manifest_cache.clear()
_component_cache.clear()
_builds_cache.clear()
_unsupported_cache.clear()