tricu

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

compiler_candidate.py (4725B)


      1 """Shared opt-in candidate workspace, with explicitly authenticated checkpoint reuse.
      2 
      3 No recovery compilation, generation promotion, or writes through source hardlinks.
      4 Only immutable CAS objects are linked; mutable checkpoint files are copied.
      5 """
      6 import hashlib
      7 import json
      8 import os
      9 from pathlib import Path
     10 import shutil
     11 import time
     12 from bootstrap_generations import source_map
     13 from collections_batch import ProbeRunner, audit_workspace
     14 from native_library_migration import digest
     15 ROOT=Path(__file__).resolve().parent.parent
     16 
     17 def sha(raw):return hashlib.sha256(raw).hexdigest()
     18 
     19 class Candidate:
     20     def __init__(self,args,closure):
     21         if not 0 < args.memory_mib <= 65536: raise ValueError('memory budget must be within 64 GiB')
     22         self.root=args.directory.resolve();self.root.mkdir(parents=True,exist_ok=args.resume)
     23         self.workspace=self.root/'workspace';self.workspace.mkdir(exist_ok=args.resume)
     24         self.runner=ProbeRunner(args.tricu,self.root,1500,memory_mib=args.memory_mib)
     25         self.report=dict(complete=False,diagnostic_reuse=args.resume,compiler_sha256=sha(args.compiler.read_bytes()),runtime_sha256=sha(args.tricu.read_bytes()),limits=dict(memory_mib=args.memory_mib,wall_seconds=1500,compile_seconds=900))
     26         self.tool=self.runner.imported(args.compiler,'tool');raw=(self.runner.store/'objects'/self.tool[:3]/self.tool).read_bytes()
     27         assert digest('arboricx.module-manifest.v1',raw)==self.tool
     28         self.producer=next(row.split('\t')[3] for row in raw.decode().splitlines()[1:] if row.split('\t')[1]=='sourceBuildGraph')
     29         if args.seed and not args.resume:
     30             seed=args.seed.resolve();previous=json.loads((seed/'report.json').read_text())
     31             assert previous['complete'] and previous['compiler_sha256']==self.report['compiler_sha256']
     32             assert audit_workspace(seed/'workspace',seed/'store',self.producer)==previous['modules']
     33             for source in (seed/'store/objects').glob('*/*'):
     34                 target=self.runner.store/'objects'/source.parent.name/source.name
     35                 target.parent.mkdir(parents=True,exist_ok=True)
     36                 if not target.exists():os.link(source,target)
     37             shutil.copytree(seed/'store/cache',self.runner.store/'cache',dirs_exist_ok=True)
     38             self.report['seed_report_sha256']=sha((seed/'report.json').read_bytes())
     39             (self.root/'seed.json').write_text(json.dumps(dict(report_sha256=self.report['seed_report_sha256'],compiler_sha256=previous['compiler_sha256']))+'\n')
     40         elif (self.root/'seed.json').exists():self.report['seed']=json.loads((self.root/'seed.json').read_text())
     41         _,self.entries,self.needed=source_map(ROOT/'bootstrap/compiler-source-map.v1',ROOT,closure)
     42         for alias in self.needed:(self.workspace/(alias+'.tri')).write_bytes((ROOT/self.entries[alias][1]).read_bytes())
     43     def build(self,alias,names=()):
     44         output=self.root/(alias+'.arboricx');argv=['compile','--file',self.workspace/(alias+'.tri'),'--toolchain',self.tool,'--tool-store',self.runner.store,'--store',self.runner.store,'--output',output,'--compile-timeout','900']
     45         for name in names:argv+=['--name',name]
     46         self.runner.run('compile-'+alias,argv,930);return output
     47     def audit(self):
     48         self.report['modules']=audit_workspace(self.workspace,self.runner.store,self.producer)
     49         for alias in self.needed:assert (self.workspace/(alias+'.tri')).read_bytes()==(ROOT/self.entries[alias][1]).read_bytes()
     50         return self.report['modules']
     51     def export(self,alias,names):
     52         output=self.root/(alias+'-selected.arboricx');argv=['export','--store',self.runner.store,'--output',output]
     53         for name in names:argv+=['--target',self.report['modules'][alias]['export_hashes'][name],'--name',name]
     54         self.runner.run('export-'+alias,argv,120);return output
     55     def invoke(self,pin,name,arguments,label=None,prefix=()):
     56         argv=list(prefix)
     57         for i,argument in enumerate(arguments):
     58             path=self.root/f'argument-{i}.tree';path.write_bytes(argument);argv+=['--arg-tree-file',path]
     59         return self.runner.invoke(label or name,pin,name,argv)
     60     def save(self):
     61         self.report['seconds']=time.monotonic()-self.runner.started
     62         (self.root/'report.json').write_text(json.dumps(self.report,indent=2)+'\n')
     63 
     64 def options(description):
     65     import argparse
     66     p=argparse.ArgumentParser(description=description);p.add_argument('tricu',type=Path)
     67     p.add_argument('--compiler',type=Path,required=True);p.add_argument('--directory',type=Path,required=True)
     68     p.add_argument('--memory-mib',type=int,default=8192)
     69     p.add_argument('--seed',type=Path);p.add_argument('--resume',action='store_true');return p