"""Keep compiler outputs while their source selection and toolchain stay compatible.""" from __future__ import annotations import json, os, shutil from pathlib import Path from common import ROOT, COMMIT, CPUS, ProbeError, sha256, write_json def cached_project(cpu: str, resolution: dict, logs: Path) -> Path: if cpu not in CPUS:raise ProbeError('Unsupported Android CPU') cache=ROOT/'build/work'/cpu for directory in (ROOT/'build',ROOT/'build/work',cache): if directory.is_symlink():raise ProbeError('Build cache must not be a symlink: '+str(directory)) cache.mkdir(parents=True,exist_ok=True) project=cache/'application' if project.is_symlink():raise ProbeError('Cached project must not be a symlink') inputs={ 'schema':1,'cpu':cpu,'upstream_commit':COMMIT, 'manifest':sha256(ROOT/'app/CastleEngineManifest.xml'), 'source_contract':sha256(ROOT/'config/extra-paths.json'), 'paths':{key:[str(path) for path in resolution[key]] for key in ('compiler_source_paths','library_paths')}, 'environment':{key:os.environ.get(key) for key in ('KAM_TOOLCHAIN_IMAGE','CASTLE_ENGINE_PATH','FPCDIR','ANDROID_NDK_ROOT', 'ANDROID_HOME','ANDROID_SDK_ROOT','JAVA_HOME')}, # Removing or adding a unit can change compiler precedence even at the same paths. 'source_files':sorted({str(path) for directory in resolution['compiler_source_paths'] for path in directory.iterdir() if path.suffix.lower() in ('.pas','.pp','.inc') and path.is_file()}), } if not os.environ.get('KAM_TOOLCHAIN_IMAGE'): inputs['tools']={} for name in ('castle-engine','fpc'): executable=shutil.which(name) inputs['tools'][name]=None if executable is None else sha256(Path(executable).resolve()) marker=cache/'inputs.json' try:previous=json.loads(marker.read_text()) except (FileNotFoundError,ValueError):previous=None reused=project.is_dir() and previous==inputs if project.exists() and not reused:shutil.rmtree(project) write_json(marker,inputs) write_json(logs/'compiler-cache.json',{'project':str(project),'reused':reused,'inputs':inputs}) return project