tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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()