mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-13 13:33:52 -06:00
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:
1 parent
4f5951e2fb
commit
3129eaf10c
14 files changed
+872
-28
No files matched your search
@@ -1047,6 +1047,11 @@ class ProfileReport:
|
||||
new_files_elided: dict[str, int] = field(default_factory=dict)
|
||||
new_hashes: list[dict] | None = None
|
||||
detection_skipped: str | None = None
|
||||
# config_reads refs written for a build a platform ships, each read at
|
||||
# that build (pin_build) rather than at source_commit: {platform,
|
||||
# revision, ref, status, detail}. A status other than BUILD_OK is review
|
||||
# work.
|
||||
build_refs: list[dict] = field(default_factory=list)
|
||||
|
||||
# Skips that say the profile cannot be checked as written: a repository
|
||||
# that no longer answers, no pin to read, a death the forge contradicts.
|
||||
@@ -1070,6 +1075,7 @@ class ProfileReport:
|
||||
+ len(self.orphaned)
|
||||
+ len(self.stale_prose or ())
|
||||
+ len(self.moved_repos)
|
||||
+ sum(1 for ref in self.build_refs if ref["status"] != "BUILD_OK")
|
||||
)
|
||||
|
||||
|
||||
@@ -1603,6 +1609,81 @@ def moved_repositories(
|
||||
return moved
|
||||
|
||||
|
||||
def shipped_build_holders(profile: dict) -> dict[int, dict]:
|
||||
"""The source_ref holders of config_reads entries naming platforms, by id.
|
||||
|
||||
Such an entry describes the build those platforms ship, so its refs are
|
||||
read at their pin_build revision, never at source_commit.
|
||||
"""
|
||||
found: dict[int, dict] = {}
|
||||
for read in profile.get("config_reads") or []:
|
||||
if not isinstance(read, dict) or not read.get("platforms"):
|
||||
continue
|
||||
found[id(read)] = read
|
||||
for key in read.get("keys") or []:
|
||||
if isinstance(key, dict):
|
||||
found[id(key)] = read
|
||||
return found
|
||||
|
||||
|
||||
def build_revision(profile: dict, platform: str) -> str | None:
|
||||
"""The emulator revision a platform's pin_build entry names, None when
|
||||
its value is no revision (a URL, a package name)."""
|
||||
for build in profile.get("pin_build") or []:
|
||||
if build.get("platform") != platform:
|
||||
continue
|
||||
value = str(build.get("value") or "").strip()
|
||||
return value if value and re.fullmatch(r"[\w.+-]+", value) else None
|
||||
return None
|
||||
|
||||
|
||||
def _check_build_refs(
|
||||
profile: dict,
|
||||
deferred: list[tuple[Citation, dict]],
|
||||
views: list[RepoView],
|
||||
fetch: Callable[[upstream.Repo, str, str], list[str] | None],
|
||||
) -> list[dict]:
|
||||
"""Each deferred config_reads ref read at the build of its platforms.
|
||||
|
||||
The file must exist at that revision and every cited line lie inside
|
||||
it. The entry declares no value to anchor on, so this is the check a
|
||||
ref past the end of its file fails (BUILD_GONE), not a content match.
|
||||
"""
|
||||
results: list[dict] = []
|
||||
for citation, read in deferred:
|
||||
for platform in read.get("platforms") or []:
|
||||
revision = str(read.get("revision") or "") or build_revision(profile, str(platform))
|
||||
row = {"platform": str(platform), "revision": revision or "",
|
||||
"ref": citation.ref, "field": citation.field}
|
||||
if revision is None:
|
||||
results.append({**row, "status": "BUILD_UNREAD",
|
||||
"detail": "no pin_build revision for this platform"})
|
||||
continue
|
||||
problems: list[str] = []
|
||||
for part in split_source_ref(citation.ref):
|
||||
role, path = ref_role(part.path)
|
||||
if " " in path:
|
||||
# A project word in front (`ROCKNIX patches/x.patch:4`):
|
||||
# a file of the platform's tree, read with the record.
|
||||
continue
|
||||
lines = next(
|
||||
(found for view in views
|
||||
if (not role or view.field == role)
|
||||
and (found := fetch(view.repo, revision, path)) is not None),
|
||||
None,
|
||||
)
|
||||
if lines is None:
|
||||
problems.append(f"{part.path} absent at {revision[:12]}")
|
||||
elif (part.end or part.start or 0) > len(lines):
|
||||
problems.append(f"{part.raw} past the end ({len(lines)} lines)")
|
||||
results.append({
|
||||
**row,
|
||||
"status": "BUILD_GONE" if problems else "BUILD_OK",
|
||||
"detail": "; ".join(problems),
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
def build_report(
|
||||
name: str, profile: dict, cache_dir: str, offline: bool = False,
|
||||
text: str | None = None,
|
||||
@@ -1621,7 +1702,13 @@ def build_report(
|
||||
)
|
||||
modes = {mode for _, mode, _ in declared_repositories(profile) if mode}
|
||||
refs = []
|
||||
at_build = shipped_build_holders(profile)
|
||||
deferred: list[tuple[Citation, dict]] = []
|
||||
for citation in collect_citations(profile, text):
|
||||
if citation.kind == "ref" and id(citation.holder) in at_build:
|
||||
# Written for the build a platform ships: read at pin_build.
|
||||
deferred.append((citation, at_build[id(citation.holder)]))
|
||||
continue
|
||||
if citation.kind == "ref":
|
||||
entry = citation.entry
|
||||
display = str(entry.get("name") or "") or citation.field
|
||||
@@ -2204,6 +2291,8 @@ def build_report(
|
||||
)
|
||||
return result
|
||||
|
||||
report.build_refs = _check_build_refs(profile, deferred, views, fetch_from)
|
||||
|
||||
staged = []
|
||||
for entry_name, ref, tokens, hashes, ref_parts, citation in refs:
|
||||
flags = [self_checked(part, tokens) for part in ref_parts]
|
||||
@@ -2335,6 +2424,14 @@ def format_report(report: ProfileReport, changed_only: bool = False) -> str:
|
||||
lines.append(f" {len(report.entries)} refs: {summary}")
|
||||
for line in report.stale_prose or ():
|
||||
lines.append(f" prose under an older pin: {line}")
|
||||
for ref in report.build_refs:
|
||||
if changed_only and ref["status"] == "BUILD_OK":
|
||||
continue
|
||||
detail = f": {ref['detail']}" if ref["detail"] else ""
|
||||
lines.append(
|
||||
f" {ref['status']} at {ref['platform']} build "
|
||||
f"{ref['revision'][:12] or '-'} {ref['ref']}{detail}"
|
||||
)
|
||||
lines.append(f" {report.needs_review()} to review")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
Reference in new issue
Block a user