T14: SF host bridge native (FluidSynth runtime DLL) + golden test

This commit is contained in:
2026-08-11 17:11:29 +07:00
parent b9817e1aed
commit 500384a226
6 changed files with 862 additions and 0 deletions
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""Tái tạo native_host/fluidsynth_runtime/ — tập DLL runtime cho SFHost (T14).
FluidSynth native bridge (SFHost.cpp) load libfluidsynth-3.dll + dependency
closure từ thư mục này (gitignore — KHÔNG commit DLL ~13MB vào git).
Cách dùng:
python scripts/dl_fluidsynth_runtime.py [--out native_host/fluidsynth_runtime]
Tải package MSYS2 mingw64 (repo.msys2.org), giải nén, copy closure DLL.
Cần network; không cần MSYS2/vcpkg cài sẵn.
"""
import argparse
import os
import re
import shutil
import tarfile
import tempfile
import urllib.request
import zstandard
BASE = "https://repo.msys2.org/mingw/mingw64/"
DEFAULT_OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"fluidsynth_runtime")
# Package chứa closure DLL của libfluidsynth-3.dll (FluidSynth 2.5.6, MSYS2 mingw64).
PACKAGES = [
"mingw-w64-x86_64-fluidsynth",
"mingw-w64-x86_64-glib2",
"mingw-w64-x86_64-libsndfile",
"mingw-w64-x86_64-gcc-libs",
"mingw-w64-x86_64-libiconv",
"mingw-w64-x86_64-gettext-runtime",
"mingw-w64-x86_64-pcre2",
"mingw-w64-x86_64-zlib-ng-compat",
"mingw-w64-x86_64-libffi",
"mingw-w64-x86_64-libogg",
"mingw-w64-x86_64-libvorbis",
"mingw-w64-x86_64-flac",
"mingw-w64-x86_64-mpg123",
"mingw-w64-x86_64-opus",
"mingw-w64-x86_64-xz",
"mingw-w64-x86_64-lame",
"mingw-w64-x86_64-wavpack",
"mingw-w64-x86_64-libwinpthread",
"mingw-w64-x86_64-portaudio",
"mingw-w64-x86_64-readline",
"mingw-w64-x86_64-termcap",
"mingw-w64-x86_64-sdl3",
]
# Closure DLL (xác minh bằng dumpbin /dependents BFS, 22 file).
# ponytail: closure snapshot theo FluidSynth 2.5.6 mingw64 — nếu upgrade
# libfluidsynth, chạy lại BFS dumpbin để cập nhật list này.
NEEDED = [
"libfluidsynth-3.dll",
"libglib-2.0-0.dll",
"libgmodule-2.0-0.dll",
"libgomp-1.dll",
"libgcc_s_seh-1.dll",
"libstdc++-6.dll",
"libsndfile-1.dll",
"libFLAC.dll",
"libogg-0.dll",
"libvorbis-0.dll",
"libvorbisenc-2.dll",
"libopus-0.dll",
"libmpg123-0.dll",
"libmp3lame-0.dll",
"libiconv-2.dll",
"libintl-8.dll",
"libpcre2-8-0.dll",
"libwinpthread-1.dll",
"libportaudio.dll",
"libreadline8.dll",
"libtermcap-0.dll",
"SDL3.dll",
]
def latest(files, prefix):
cands = [f for f in files
if f.startswith(prefix + "-") and f.endswith(".pkg.tar.zst") and ".sig" not in f]
return sorted(cands)[-1]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", default=DEFAULT_OUT)
ap.add_argument("--cache", default=None, help="thư mục chứa .pkg.tar.zst đã tải")
args = ap.parse_args()
cache = args.cache or tempfile.mkdtemp(prefix="msys2_pkgs_")
stage = tempfile.mkdtemp(prefix="msys2_stage_")
print("== liệt kê mirror")
html = urllib.request.urlopen(BASE, timeout=60).read().decode("utf-8", "ignore")
files = re.findall(r'href="([^"]+\.pkg\.tar\.zst)"', html)
dctx = zstandard.ZstdDecompressor()
for pkg in PACKAGES:
fname = latest(files, pkg)
path = os.path.join(cache, fname)
if not os.path.exists(path):
print("== tải", fname)
urllib.request.urlretrieve(BASE + fname, path)
else:
print("== có sẵn", fname)
with open(path, "rb") as f:
with dctx.stream_reader(f) as r:
with tarfile.open(fileobj=r, mode="r|") as tf:
for m in tf:
if m.isfile():
tf.extract(m, stage, filter="data")
bin_dir = os.path.join(stage, "mingw64", "bin")
os.makedirs(args.out, exist_ok=True)
missing = []
for dll in NEEDED:
src = os.path.join(bin_dir, dll)
if not os.path.exists(src):
missing.append(dll)
continue
shutil.copy2(src, os.path.join(args.out, dll))
print("== copy", dll)
if missing:
raise SystemExit("THIẾU DLL trong stage: " + ", ".join(missing))
print("OK —", len(NEEDED), "DLL ->", args.out)
if __name__ == "__main__":
main()