digest_byte.py (4854B)
1 #!/usr/bin/env python3 2 """Rebuild source_object and test digest bytes against independent host models. 3 4 Dependencies are reused only after checking their frozen sources against the 5 working tree. Probe exports are confined to the disposable workspace. This does 6 not publish or accept a compiler generation. 7 """ 8 import argparse 9 import hashlib 10 import json 11 from pathlib import Path 12 import re 13 import subprocess 14 15 from bundle_dag import merge_bundles, read 16 from fixture_sources import snapshot_bytes, verify 17 from native_header import native_header 18 from pattern_lowering import audit 19 from shipped_compiler import verify as verify_compiler 20 21 ROOT = Path(__file__).resolve().parent.parent 22 23 24 def main(): 25 parser = argparse.ArgumentParser(description=__doc__) 26 parser.add_argument('tricu', type=Path) 27 parser.add_argument('--directory', type=Path, required=True) 28 args = parser.parse_args() 29 root = args.directory.resolve() 30 root.mkdir(parents=True, exist_ok=False) 31 workspace = root / 'workspace' 32 workspace.mkdir() 33 (workspace / 'tricu.workspace').write_text('tricu.workspace.v1\n') 34 store = root / 'store' 35 host = args.tricu.resolve() 36 compiler = verify_compiler() 37 source = (ROOT / 'lib/source/object.tri').read_bytes() 38 rows = {r[2]: r for r in verify(ROOT / 'test/fixtures')} 39 paths = {r.split('\t')[1]: r.split('\t')[3] for r in 40 (ROOT / 'bootstrap/compiler-source-map.v1').read_text().splitlines()[1:]} 41 report = dict(complete=False, compiler_sha256=compiler['sha256'], 42 runtime_sha256=hashlib.sha256(host.read_bytes()).hexdigest(), 43 source_sha256=hashlib.sha256(source).hexdigest(), reused={}, commands=[]) 44 45 def command(argv, timeout=180): 46 cmd = list(map(str, argv)) 47 out = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True, timeout=timeout) 48 report['commands'].append(dict(argv=cmd, code=out.returncode, 49 stdout=out.stdout, stderr=out.stderr)) 50 (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') 51 if out.returncode: 52 raise RuntimeError(out.stdout[-4000:] + out.stderr[-4000:]) 53 return out.stdout 54 55 def run(argv): 56 return command([host, 'arboricx', *argv, '+RTS', '-N1', '-M4096m', '-RTS']) 57 58 dependencies = native_header(source.decode())[0] 59 checked = set() 60 61 def check_source(alias): 62 if alias in checked: 63 return 64 checked.add(alias) 65 current = (ROOT / paths[alias]).read_bytes() 66 assert snapshot_bytes(ROOT / 'test/fixtures', rows[alias][4]) == current, ('stale dependency', alias) 67 for dep in native_header(current.decode())[0]: 68 check_source(dep) 69 70 for alias in dependencies: 71 check_source(alias) 72 cohort, _, _, _, source_pin = rows[alias] 73 fixture = ROOT / 'test/fixtures' / (cohort + '.arboricx') 74 bundle = root / (alias + '.arboricx') 75 merge_bundles(bundle, [(fixture, [(name, name[len(alias)+1:]) for name in read(fixture)[3] 76 if name.startswith(alias + '.')])], prune_unreachable=True) 77 pin = re.search(r'Created module alias .* -> ([0-9a-f]{64})', 78 run(['import', '--file', bundle, '--store', store, '--indexed']))[1] 79 (workspace / (alias + '.tri')).write_text('!export * from ' + pin + '\n') 80 report['reused'][alias] = dict(manifest=pin, source_pin=source_pin) 81 (workspace / 'source_object.tri').write_bytes( 82 b'!export sourceObjectCanonicalByte\n!export sourceObjectHashByte?\n' + source) 83 candidate = root / 'source_object.arboricx' 84 run(['compile', '--file', workspace / 'source_object.tri', '--store', store, 85 '--output', candidate, '--compile-timeout', '150']) 86 report['modules'] = audit(workspace, store, compiler['sha256'], report['reused']) 87 expr = ('let f=builtins.getFlake "git+file://' + str(ROOT) + '";' 88 'p=f.inputs.nixpkgs.legacyPackages.x86_64-linux;' 89 'in p.haskellPackages.ghcWithPackages(h:[h.cryptonite h.memory h.base16-bytestring h.vector])') 90 command(['nix', 'shell', '--impure', '--expr', expr, '--command', 'ghc', '-O2', '-rtsopts', 91 '-XOverloadedStrings', '-XLambdaCase', '-XMultiWayIf', '-XScopedTypeVariables', 92 '-isrc', '-outputdir', root / 'objects', ROOT / 'test/DigestByte.hs', 93 '-o', root / 'controls'], 300) 94 command([root / 'controls', candidate, '+RTS', '-M2048m', '-RTS']) 95 report.update(complete=True, controls_sha256=hashlib.sha256((ROOT / 'test/DigestByte.hs').read_bytes()).hexdigest()) 96 (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') 97 print('PASS digest-byte canonical, legacy, length, demand and provenance controls') 98 99 100 if __name__ == '__main__': 101 main()