refactor: read the fbneo version from version.h

FBNeo bumps src/burn/version.h without tagging: the scraper wrote the
last tag, v1.0.0.02, while the source said 1.0.0.03. It now reads the
header the way the libretro core prints it. The merge no longer writes
core_version: the hashes come from finalburnneo/FBNeo's tip, which the
libretro/FBNeo pin does not name, so the version stays the one
version_basis reads at that pin. A MAME release profile is written with
its version_basis.
This commit is contained in:
Abdessamad Derraz committed 2026-10-27 18:48:16 +01:00
1 parent 6aa236440a
commit 07c82c885f
5 files changed
+51 -80

No files matched your search

+10 -6
View File
@@ -158,7 +158,12 @@ def merge_fbneo_profile(
profile = _load_yaml(profile_path)
hashes = _load_json(hashes_path)
profile["core_version"] = hashes.get("version", profile.get("core_version"))
# core_version stays as written: it is the version of the code at the
# profile's own pin, read through version_basis (profile_sync
# --set-version). The hashes come from finalburnneo/FBNeo's tip, a
# revision the libretro/FBNeo pin does not name: writing its version
# left fbneo, fbneo_cps12 and fbneo_neogeo on a release their pin
# never was.
files = profile.get("files", [])
archive_files, non_archive = _split_files(files, lambda f: "archive" in f)
@@ -1060,17 +1065,16 @@ def _backup_and_write_fbneo(path: str, data: dict, hashes: dict) -> None:
"""Write merged FBNeo profile using text-based patching.
FBNeo profiles have individual ROM entries with archive: field.
Patches core_version, the content values of existing entries that
changed, and appends new ROM entries. Advancing core_version alone left
a profile claiming a release whose crc32 it did not carry. Comments and
curated source_refs stay as written.
Patches the content values of existing entries that changed and
appends new ROM entries. core_version is the pin's, never written here.
Comments and curated source_refs stay as written.
"""
p = Path(path)
backup = p.with_suffix(PROFILE_BACKUP_SUFFIX)
shutil.copy2(p, backup)
original = p.read_text(encoding="utf-8")
patched = _patch_core_version(original, data.get("core_version", ""))
patched = original
# Identify new ROM entries by comparing parsed data keys, not text search
existing_data = yaml_load(original) or {}
+26 -70
View File
@@ -11,6 +11,7 @@ import argparse
import json
import logging
import os
import re
import shutil
import subprocess
import sys
@@ -20,7 +21,7 @@ from typing import Any
import yaml
from common import github_token, source_yaml_paths, write_text_atomic, yaml_load
from common import source_yaml_paths, write_text_atomic, yaml_load
from scripts.scraper._hash_merge import compute_diff, merge_fbneo_profile
from scripts.scraper.fbneo_parser import parse_fbneo_source_tree
@@ -51,7 +52,7 @@ def _is_cache_fresh() -> bool:
def _sparse_clone() -> None:
"""Sparse clone FBNeo repo, checking out only src/burn/drv."""
"""Sparse clone FBNeo repo: src/burn/drv, and in cone mode src/burn/version.h."""
if CLONE_DIR.exists():
shutil.rmtree(CLONE_DIR)
@@ -74,7 +75,7 @@ def _sparse_clone() -> None:
)
subprocess.run(
["git", "sparse-checkout", "set", "src/burn/drv", "src/burner/resource.h"],
["git", "sparse-checkout", "set", "src/burn/drv"],
cwd=CLONE_DIR,
check=True,
capture_output=True,
@@ -82,31 +83,28 @@ def _sparse_clone() -> None:
)
# src/burn/version.h, as the libretro core prints it: "v%x.%x.%x.%02x" of
# the BCD BURN_VERSION, so VER_ALPHA 3 reads 1.0.0.03. FBNeo bumps this file
# without tagging: the last tag (v1.0.0.02) was 9 337 commits behind it.
_VERSION_H = re.compile(
r"#define\s+VER_MAJOR\s+(\d+)\s+#define\s+VER_MINOR\s+(\d+)\s+"
r"#define\s+VER_BETA\s+(\d+)\s+#define\s+VER_ALPHA\s+(\d+)"
)
def version_from_header(text: str) -> str:
"""The version src/burn/version.h states."""
found = _VERSION_H.search(text)
if not found:
raise RuntimeError("src/burn/version.h states no VER_MAJOR..VER_ALPHA")
major, minor, beta, alpha = (int(group) for group in found.groups())
return f"v{major}.{minor}.{beta}.{alpha:02d}"
def _extract_version() -> tuple[str, str]:
"""Extract version tag and commit SHA from the cloned repo.
Returns (version, commit_sha). Falls back to resource.h if no tag.
"""
result = subprocess.run(
["git", "describe", "--tags", "--abbrev=0"],
cwd=CLONE_DIR,
capture_output=True,
text=True,
)
# Prefer real version tags over pseudo-tags like "latest"
version = "unknown"
if result.returncode == 0:
tag = result.stdout.strip()
if tag and tag != "latest":
version = tag
# Fallback: resource.h
if version == "unknown":
version = _version_from_resource_h()
# Last resort: use GitHub API for latest real release tag
if version == "unknown":
version = _version_from_api()
"""The version the cloned source states, and its commit."""
header = CLONE_DIR / "src" / "burn" / "version.h"
version = version_from_header(header.read_text(encoding="utf-8", errors="replace"))
sha_result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=CLONE_DIR,
@@ -114,49 +112,7 @@ def _extract_version() -> tuple[str, str]:
text=True,
check=True,
)
commit = sha_result.stdout.strip()
return version, commit
def _version_from_api() -> str:
"""The latest real release tag, or an error: "unknown" was written as
core_version and cached for a day."""
import json as json_mod
import urllib.error
import urllib.request
headers = {"User-Agent": "retrobios-scraper/1.0"}
token = github_token()
if token:
headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(
"https://api.github.com/repos/finalburnneo/FBNeo/tags?per_page=10", headers=headers
)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
tags = json_mod.loads(resp.read())
except (urllib.error.URLError, OSError, ValueError) as exc:
raise RuntimeError(f"cannot determine the FBNeo version: {exc}") from exc
for tag in tags:
if tag["name"] != "latest" and tag["name"].startswith("v"):
return tag["name"]
raise RuntimeError("cannot determine the FBNeo version: no tag, resource.h or release")
def _version_from_resource_h() -> str:
"""Fallback: parse VER_FULL_VERSION_STR from resource.h."""
resource_h = CLONE_DIR / "src" / "burner" / "resource.h"
if not resource_h.exists():
return "unknown"
text = resource_h.read_text(encoding="utf-8", errors="replace")
for line in text.splitlines():
if "VER_FULL_VERSION_STR" in line:
parts = line.split('"')
if len(parts) >= 2:
return parts[1]
return "unknown"
return version, sha_result.stdout.strip()
def _cleanup() -> None:
+7
View File
@@ -467,6 +467,13 @@ def release_profile(
"source_commit": source_commit,
"upstream_commit": upstream_commit,
"core_version": version,
# Where profile_sync reads it again: the makefile of the libretro/mame
# tree the platform builds, at source_commit.
"version_basis": {
"kind": "file",
"path": "makefile",
"pattern": '#define BARE_BUILD_VERSION "([^"]+)"',
},
"display_name": f"Arcade (MAME {version})",
"cores": cores,
"built_by": built_by,