mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
build_xlate.py --id-only wrote an nx2navi_xlate.id for whatever exe sat in the folder, from the sources as they are, so make_launcher's guard (check_xlate, which compares only the .id) passed an old exe beside a fresh .id. Nothing calls the option; it is gone, and only a build writes an .id. make_release.check_translator compared build/xlate's .id with build_xlate.identity(), which hashes the tree the last build prepared there, so a patch or vendor change since that build passed unseen; make_appimage.py and make_kit.py checked nothing. The fresh-prepare identity moves from make_launcher into build_xlate as source_identity(), and build_xlate.check_built(folder, exe) refuses a missing exe or .id and an .id that differs from it. make_launcher, make_release, make_appimage and make_kit stop on its reason, each before its downloads, builds or deletions: make_launcher's check moves from build_app to the top of main, before the --out folder is deleted and the toolchain lock is resolved. A translator folder built from the current sources, as the release scripts pass it (work\xlate_6866a13e), passes as before. Not reproduced before the change beyond the audit's probes. tests/xlate_package_check.py (new) checks a matching folder, missing files, another identity and a stale build (refused although identity() over its own tree equals its .id), that --id-only is refused, and that make_launcher, make_release and make_kit stop on a foreign translator, the kit before any download; check_built passes on work\xlate_6866a13e. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
5.8 KiB
Python
144 lines
5.8 KiB
Python
"""Check the shader translator guard of the packaging scripts (tools/build_xlate.py source_identity and check_built,
|
|
used by tools/make_launcher.py check_xlate, tools/make_release.py check_translator, tools/make_kit.py and
|
|
tools/make_appimage.py):
|
|
|
|
- a folder whose nx2navi_xlate.id names this checkout's sources passes; a missing exe or .id is refused;
|
|
- a folder built before a patch changed is refused, although identity() over its own prepared tree (what
|
|
make_release.py compared before) equals its .id;
|
|
- build_xlate.py has no option that writes an .id beside an exe it did not build (--id-only is gone);
|
|
- make_launcher.check_xlate, make_release.check_translator and make_kit stop on a foreign translator, make_kit before
|
|
it downloads or deletes anything; make_launcher and make_appimage run the check before their slow steps.
|
|
|
|
Needs git on PATH (prepare_sources applies the patches with it) and tools/vendor. No game files; any Python 3.10+.
|
|
|
|
Usage: xlate_package_check.py
|
|
"""
|
|
import contextlib
|
|
import inspect
|
|
import io
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT / 'tools'))
|
|
import build_xlate # noqa: E402
|
|
|
|
EXE = 'nx2navi_xlate.exe'
|
|
|
|
|
|
def folder(base, name, ident, exe=True):
|
|
d = base / name
|
|
d.mkdir(parents=True)
|
|
if exe:
|
|
(d / EXE).write_bytes(b'MZ stand-in, not a translator')
|
|
if ident is not None:
|
|
(d / 'nx2navi_xlate.id').write_text(ident + '\n', encoding='ascii')
|
|
return d
|
|
|
|
|
|
def refused(fn, *args):
|
|
try:
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
fn(*args)
|
|
except SystemExit as e:
|
|
return str(e.code)
|
|
return None
|
|
|
|
|
|
def main():
|
|
if not shutil.which('git'):
|
|
print('xlate package check: git is not on PATH; skipped')
|
|
return 0
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
want = build_xlate.source_identity()
|
|
assert len(want) == 64 and build_xlate.BUILD == ROOT / 'build' / 'xlate', 'source_identity restores BUILD'
|
|
|
|
with tempfile.TemporaryDirectory(prefix='xlate-pkg-') as t:
|
|
base = Path(t)
|
|
good = folder(base, 'good', want)
|
|
no_exe = folder(base, 'no_exe', want, exe=False)
|
|
no_id = folder(base, 'no_id', None)
|
|
other = folder(base, 'other', '4d051299931d390e' + '0' * 48)
|
|
|
|
# A folder built before a patch changed: its prepared tree differs from a fresh one by one file, and its .id
|
|
# is identity() over that tree, as build_xlate.main wrote it then.
|
|
stale = base / 'stale'
|
|
old = build_xlate.BUILD
|
|
build_xlate.BUILD = stale
|
|
try:
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
tree = build_xlate.prepare_sources()
|
|
victim = sorted(p for p in (tree / 'src').rglob('*.cs'))[0]
|
|
victim.unlink()
|
|
stale_id = build_xlate.identity()
|
|
finally:
|
|
build_xlate.BUILD = old
|
|
(stale / EXE).write_bytes(b'MZ stand-in, not a translator')
|
|
(stale / 'nx2navi_xlate.id').write_text(stale_id + '\n', encoding='ascii')
|
|
assert stale_id != want
|
|
|
|
def check(d):
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
return build_xlate.check_built(d, EXE)
|
|
|
|
assert check(good) is None
|
|
assert 'missing' in check(no_exe)
|
|
assert 'missing' in check(no_id)
|
|
assert 'other sources' in check(other)
|
|
assert 'other sources' in check(stale)
|
|
print('check_built: this checkout\'s translator passes; missing files, another identity and a stale build '
|
|
'are refused')
|
|
|
|
# --id-only is gone: the option is refused and writes nothing.
|
|
bare = folder(base, 'bare', None)
|
|
r = subprocess.run([sys.executable, str(ROOT / 'tools' / 'build_xlate.py'), '--id-only', '--out', str(bare)],
|
|
capture_output=True, text=True)
|
|
assert r.returncode != 0 and not (bare / 'nx2navi_xlate.id').exists(), r.stdout + r.stderr
|
|
print('build_xlate.py: --id-only refused, no .id written')
|
|
|
|
# The packaging scripts, with the fresh identity computed once.
|
|
build_xlate.source_identity = lambda: want
|
|
import make_launcher
|
|
import make_release
|
|
import make_kit
|
|
assert refused(make_launcher.check_xlate, good) is None
|
|
for bad in (other, stale, no_exe):
|
|
assert refused(make_launcher.check_xlate, bad)
|
|
main_root = make_release.MAIN_ROOT
|
|
try:
|
|
for d, ok in ((good, True), (stale, False), (other, False)):
|
|
rel = base / f'rel_{d.name}'
|
|
shutil.copytree(d, rel / 'build' / 'xlate')
|
|
make_release.MAIN_ROOT = rel
|
|
assert (refused(make_release.check_translator) is None) == ok, d.name
|
|
finally:
|
|
make_release.MAIN_ROOT = main_root
|
|
out = base / 'kit_out'
|
|
argv = sys.argv
|
|
try:
|
|
sys.argv = ['make_kit.py', '--xlate', str(stale), '--out', str(out)]
|
|
why = refused(make_kit.main)
|
|
finally:
|
|
sys.argv = argv
|
|
assert why and 'other sources' in why and not out.exists()
|
|
print('make_launcher, make_release and make_kit stop on a foreign translator (make_kit before any download)')
|
|
|
|
# The check comes before the slow steps.
|
|
src = inspect.getsource(make_launcher.main)
|
|
assert src.index('check_xlate(a.xlate)') < src.index('shutil.rmtree(out') < src.index('toolchain.make_lock')
|
|
import make_appimage
|
|
src = inspect.getsource(make_appimage.main)
|
|
assert src.index('build_xlate.check_built(') < src.index('dlss_pin.resolve(') < src.index('shutil.rmtree(app')
|
|
src = inspect.getsource(make_release.main)
|
|
assert src.index('check_translator()') < src.index('shutil.rmtree(out)')
|
|
print('xlate package check: passed')
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|