feat: bind config launcher records on evidence

A launcher record citing a config, option or feature file binds only
when the platform's code installs the file (installs), its launch code
was read for what replaces it (overrides), and the emulator's profile
shows where the shipped build loads it and reads each key
(config_reads, read at pin_build). Unproven records place no file and
hand no directory. verify, cross_reference and the platform page list
them apart, outside severity and --strict.
This commit is contained in:
Abdessamad Derraz committed 2026-11-11 11:03:47 +01:00
1 parent 4f5951e2fb
commit 3129eaf10c
14 files changed
+872 -28

No files matched your search

+45 -2
View File
@@ -56,9 +56,11 @@ from common import (
from hashing import parse_hash_list
from launchers import (
LauncherLayer,
bound_launchers,
catalogued_model_files,
declared_destinations,
held_record,
launcher_unproven,
name_settings,
set_by,
setting_readers,
@@ -1095,6 +1097,8 @@ def _settle_launcher_places(
# The --json key of the launcher reads category, beside the emulator keys.
LAUNCHER_READS_KEY = "_launcher_reads_not_held"
# The --json key of the launcher records a config file does not prove.
LAUNCHER_UNPROVEN_KEY = "_launcher_unproven"
# A package record names no file: it records what a package builds.
_NAMELESS_KINDS = frozenset({"package"})
@@ -1238,7 +1242,9 @@ def launcher_reads_not_held(
rows: list[dict] = []
for platform, config in configs.items():
by_key: dict[str, dict] = {}
for record in config.get("launchers") or []:
# A record resting on a config file it cannot prove reads nothing
# yet: it is reported apart (launcher_unproven).
for record in bound_launchers(config, profiles):
names = launcher_file_names(record)
if not names:
continue
@@ -1331,6 +1337,25 @@ def launcher_reads_not_held(
return rows
def print_launcher_unproven(rows: list[dict]) -> None:
"""The launcher records resting on a config file the code does not prove.
Neither a pack, a manifest nor a severity reads them: each is listed
with the links it lacks.
"""
if not rows:
return
platforms = sorted({r["platform"] for r in rows})
noun = "platform" if len(platforms) == 1 else "platforms"
print(f"\nLauncher records unproven: {len(rows)} across {len(platforms)} {noun}")
for platform in platforms:
print(f"\n{platform}")
for row in (r for r in rows if r["platform"] == platform):
print(f" [{row['index']}] {row['emulator']} {row['kind']}: {row['passes_to']}")
for link in row["missing"]:
print(f" missing {link}")
def print_launcher_reads(rows: list[dict]) -> None:
"""The launcher reads gap category, after the profile gaps.
@@ -1649,8 +1674,25 @@ def main() -> None:
db, all_profiles, registry, emulator or "",
)
unproven = [
row for row in launcher_unproven(
{
name: load_platform_config(name, args.platforms_dir)
for name in (selected or list_registered_platforms(args.platforms_dir))
},
all_profiles,
)
if not emulator or any(
emulator in LauncherLayer._profiles_named(part, all_profiles, set(all_profiles))
for part in row["emulator"].split(", ")
)
]
if args.json:
print(json.dumps({**report, LAUNCHER_READS_KEY: launcher_reads}, indent=2))
print(json.dumps(
{**report, LAUNCHER_READS_KEY: launcher_reads, LAUNCHER_UNPROVEN_KEY: unproven},
indent=2,
))
else:
if emulator:
scope = emulator
@@ -1660,6 +1702,7 @@ def main() -> None:
scope = "all emulators"
print_report(report, scope, show_clean=bool(args.emulator))
print_launcher_reads(launcher_reads)
print_launcher_unproven(unproven)
if __name__ == "__main__":