local-module-native.py (3612B)
1 #!/usr/bin/env python3 2 """Verify browser-produced exports with the native CLI and shipped compiler. 3 4 Input is a local-module browser probe JSON report (BENCHMARK_REPORT). 5 Expected identities come from independent native compilation using a Nix-built host. 6 """ 7 import argparse 8 import json 9 from pathlib import Path 10 import re 11 import subprocess 12 import sys 13 import tempfile 14 ROOT = Path(__file__).resolve().parents[3] 15 SITE = ROOT / 'ext/demo-site' 16 sys.path.insert(0, str(ROOT / 'test')) 17 from shipped_compiler import verify 18 from bundle_dag import read 19 20 21 def main(): 22 parser = argparse.ArgumentParser(description=__doc__) 23 parser.add_argument('tricu', type=Path) 24 parser.add_argument('reports', nargs='+', type=Path) 25 args = parser.parse_args() 26 metadata = verify() 27 releases = json.loads((SITE / 'packages.json').read_text())['releases'] 28 identities = set() 29 with tempfile.TemporaryDirectory(prefix='tricu-local-export-') as temporary: 30 work = Path(temporary) 31 (work / 'tricu.workspace').write_text('tricu.workspace.v1\n') 32 def run(*words): 33 reply = subprocess.run([str(args.tricu.resolve()), *map(str, words), '+RTS', '-N1', '-M2048m', '-RTS'], 34 cwd=work, capture_output=True, timeout=120) 35 assert reply.returncode == 0, (reply.stdout + reply.stderr).decode() 36 return reply.stdout.decode().strip() 37 def imported(file, store): 38 return re.search(r'Created module alias .* -> ([0-9a-f]{64})', run('arboricx', 'import', '--indexed', '--file', file, '--store', store))[1] 39 compiler = imported(ROOT / metadata['bundle'], work / 'tools') 40 for report in args.reports: 41 data = json.loads(report.read_text()) 42 file = work / 'browser.arboricx' 43 file.write_bytes(bytes(data['bytes'])) 44 manifest = imported(file, work / 'consumer') 45 assert manifest == data['manifest'] 46 assert list(read(file)[3]) == ['echo', 'plus', 'value'] 47 def natural(n): return (b'\x02' + (b'\x01\x00' if n % 2 else b'\x00') + natural(n // 2)) if n else b'\x00' 48 for name, number, expected in [('plus', 33, '42'), ('value', None, '42'), ('echo', 17, '17')]: 49 arguments = [] 50 if number is not None: 51 arg = work / 'argument.tree'; arg.write_bytes(natural(number)) 52 arguments = ['--arg-tree-file', arg] 53 assert run('arboricx', 'run', '--module', manifest, '--store', work / 'consumer', '--name', name, *arguments, '--format', 'number') == expected 54 for ref in re.findall(r'^!import ([0-9a-f]{64}) ', data['source'], re.M): 55 release = next(r for r in releases if r['manifest'] == ref) 56 assert imported(SITE / 'bundles' / (release['hash'] + '.arboricx'), work / 'build') == ref 57 source = work / 'original.tri'; source.write_text(data['source']) 58 native = work / 'native.arboricx' 59 run('arboricx', 'compile', '--file', source, '--store', work / 'build', '--toolchain', compiler, 60 '--tool-store', work / 'tools', '--output', native, '--compile-timeout', '110') 61 assert imported(native, work / 'native-consumer') == manifest, 'Native compiled interface/tree identities differ' 62 identities.add(manifest) 63 assert len(identities) == 1, 'Backend export identities differ' 64 print('Native import/run and independently compiled complete module identity agree for', len(args.reports), 'browser reports') 65 66 67 if __name__ == '__main__': 68 main()