bootstrap_retention_probe.py (7375B)
1 #!/usr/bin/env python3 2 """Isolated retained-toolchain probes, invoked only by bootstrap_retention.py. 3 No compiler generation checkpoint or fixture publication is created here. 4 """ 5 import argparse 6 import hashlib 7 import json 8 from pathlib import Path 9 import subprocess 10 import sys 11 12 from bootstrap_retention import REPO, sha, save, check_support_source, support_preparations 13 from bootstrap_acceptance import verify 14 from generation_io import versions 15 16 17 def portable_probe(source, io_version): 18 """Select the CLI oracle before execution; historical runtimes keep their oracle.""" 19 if io_version == 2: 20 return REPO / 'test/portable_cli.py', ['--compiler-io-version', '2'] 21 if io_version == 1: 22 oracle = source / 'test/portable_cli.py' 23 if not oracle.is_file(): 24 raise ValueError('retained IO v1 source requires its frozen portable CLI oracle') 25 return oracle, [] # Historical harnesses default to IO v1. 26 raise ValueError('unsupported retained compiler IO version') 27 28 29 def main(): 30 p = argparse.ArgumentParser(description=__doc__) 31 p.add_argument('--root', type=Path, required=True) 32 p.add_argument('--plan', type=Path, required=True) 33 p.add_argument('--directory', type=Path, required=True) 34 a = p.parse_args() 35 plan = json.loads(a.plan.read_text()); config = plan['config'] 36 root, out = a.root, a.directory 37 # Refuse a non-isolated invocation; every removal target must be invisible. 38 if any((root / row['objects']).exists() for row in plan['stores']): 39 raise ValueError('obsolete object stores are still visible') 40 report = dict(complete=False, obsolete_stores_hidden=True, toolchains=[]) 41 def run(label, command, seconds=900): 42 with (out / (label + '.log')).open('w') as log: 43 subprocess.run(command, stdout=log, stderr=subprocess.STDOUT, check=True, timeout=seconds) 44 try: 45 for t in config['toolchains']: 46 role = t['role']; generation = root / t['generation']; source = root / t['source'] 47 acceptance = root / t['acceptance']; runtime = t['runtime'] 48 accepted = json.loads(acceptance.read_text()) 49 # These cohorts still include first generation, so rerun full-pair 50 # acceptance as well as the generation-two audit. Portable-only 51 # retirement of first generation needs an explicitly different gate. 52 io_version = versions(accepted['inputs'][1])[1] 53 fresh = verify(Path(accepted['generations'][0]), generation, Path(runtime), source) 54 if fresh['artifacts'] != accepted['artifacts']: 55 raise ValueError('retained acceptance artifacts differ') 56 oracle, io_flags = portable_probe(source, io_version) 57 run(role + '-portable', [sys.executable, str(oracle), runtime, 58 '--backend', 'stable', '--indexed', *io_flags, '--compiler-bundle', str(generation / 'build-stable.arboricx')]) 59 run(role + '-smoke', [sys.executable, str(REPO / 'test/bootstrap_smoke.py'), runtime, 60 '--generation', str(generation), '--acceptance', str(acceptance), 61 '--source-root', str(source), '--directory', str(out / (role + '-smoke'))]) 62 scoped = out / (role + '-map') 63 run(role + '-map', [sys.executable, str(REPO / 'test/bootstrap_generations.py'), 64 str(generation), str(scoped), '--tricu', runtime, '--source-root', str(source), 65 '--map', str(generation / 'source-map.v1'), '--root', 'native_math', '--backend', 'stable', 66 '--fresh', '--produced-io-version', str(io_version), '--capabilities', '2', 67 '--memory-mib', '8192', '--wall-seconds', '900']) 68 run(role + '-map-audit', [sys.executable, str(REPO / 'test/bootstrap_store_check.py'), 69 str(scoped), '--source-root', str(source), '--root', 'native_math']) 70 old = (generation / 'cas/cache/source-build/native_math').read_text().split('\t')[2] 71 new = (scoped / 'cas/cache/source-build/native_math').read_text().split('\t')[2] 72 if old != new: 73 raise ValueError('scoped map-builder changed complete native_math manifest') 74 smoke = json.loads((out / (role + '-smoke/report.json')).read_text()) 75 if not smoke['complete'] or not smoke['wrong_output_rejected']: 76 raise ValueError('incomplete retained compiler smoke') 77 report['toolchains'].append(dict(role=role, artifacts=fresh['artifacts'], 78 acceptance_sha256=sha(acceptance), runtime_sha256=sha(runtime), 79 fresh_pair_audits=fresh['audits'], portable_cli=True, 80 portable_oracle=str(oracle), portable_oracle_sha256=sha(oracle), 81 complete_results=len(smoke['cases']), scoped_map_manifest=new)) 82 support = config['support'] 83 runtime = next(t['runtime'] for t in config['toolchains'] if t['role'] == support['toolchain']) 84 source = check_support_source(root, support) 85 publication = out / 'support-reproduced' 86 command = [sys.executable, str(REPO / 'test/repair_support_fixture.py'), runtime, 87 *[str(root / path) for path in support_preparations(support)], '--output-directory', str(publication), 88 '--source-root', str(source), '--capabilities', '2', '--verification-memory-mib', '8192'] 89 if support.get('relocate_patterns'): 90 command += ['--relocate-patterns'] 91 if support.get('compiler_io_cohort'): 92 command += ['--compiler-io-cohort'] 93 if support.get('add_binary64_support'): 94 command += ['--add-binary64-support'] 95 if support.get('add_io_support'): 96 command += ['--add-io-support'] 97 run('support', command, seconds=1500) 98 compared = {} 99 for name in ['support-library.arboricx', 'support-rebuilt.arboricx', 'support-library.v1']: 100 if sha(publication / name) != sha(REPO / 'test/fixtures' / name): 101 raise ValueError('current support fixture did not reproduce: ' + name) 102 compared[name] = sha(publication / name) 103 expected = REPO / 'test/fixtures/support-receipts' 104 actual = publication / 'support-receipts' 105 if {p.name: sha(p) for p in actual.iterdir()} != {p.name: sha(p) for p in expected.iterdir()}: 106 raise ValueError('support receipt/manifests did not reproduce') 107 run('fixture-freshness', [sys.executable, str(REPO / 'test/fixture_sources.py'), 'current', '--source-root', str(source)]) 108 from fixture_sources import current_mismatches 109 working_differences = current_mismatches(REPO / 'test/fixtures', REPO) 110 report.update(fixture_source_scope='frozen-reference' if 'source' in support else 'working-tree', 111 fixture_source=support.get('source'), 112 working_source_differences=working_differences) 113 report.update(complete=True, support_sha256=compared, 114 support_receipts_reproduced=True, fixture_sources_match_selected=True, 115 fixture_sources_current=not working_differences) 116 finally: 117 save(out / 'report.json', report) 118 print('PASS isolated pair audits, CLI integrity/diagnostics, correctness smokes, scoped map-builds and support reproduction') 119 120 121 if __name__ == '__main__': 122 main()