Compilación Incremental — Caché Persistente en Disco
Archivo: src/incremental_cache.rs (241 líneas)
¿Por qué existe?
Evita recompilar módulos cuyo código fuente no cambió. Almacena bytecode serializado de cada módulo en .forja/cache/ y lo reutiliza en ejecuciones subsiguientes, comparando el hash del fuente guardado con el hash actual. La persistencia cruza corridas del compilador, no solo memoria.
Arquitectura
La caché es un HashMap<String, CachedModule> (ruta de módulo → entrada) que se carga desde manifest.json al construir y se vuelca a disco con save_to_disk. Cada entrada guarda el hash del fuente, la versión del formato (CACHE_VERSION, actualmente 1), el bytecode binario en formato .fbc y la lista de imports para invalidación transitiva.
| Componente | Descripción |
|---|---|
CachedModule | Entrada serializable: source_hash, cache_version, bytecode_bin y imports |
IncrementalCache | Caché en disco en .forja/cache/ con estadísticas hits, misses e invalidated |
CacheStats | Estadísticas: entradas totales, hits, misses, invalidados y hit_rate |
API Pública
| Función | Descripción |
|---|---|
get(module_path, source_hash) -> Option<Vec<Opcode>> | Devuelve bytecode si el hash coincide y la versión es actual; deserializa con deserializar_bytecode |
put(module_path, source_hash, opcodes, imports) | Serializa el bytecode (serializar_bytecode) y guarda la entrada |
invalidate(module_path) | Borra el módulo y todos sus dependientes transitivos (los que lo importan) con DFS |
invalidate_changed() -> Vec<String> | Re-hashea cada fuente con ModuleResolver::hash_fuente e invalida los que cambiaron |
save_to_disk() / load_from_disk() | Persisten/cargan el manifest.json con serde |
clear() / stats() | Limpia toda la caché y expone estadísticas (incluido hit rate) |
Cómo se integra
El pipeline de compilación calcula el hash de cada fuente (ModuleResolver::hash_fuente), consulta get para reutilizar bytecode sin recompilar, y guarda con put los módulos recién compilados. invalidate_changed detecta cambios de fuente y la invalidación transitiva garantiza que, si base.fa cambia, lib.fa (que lo importa) también se recompile.
invalidate usa un stack DFS con HashSet de visitados: la invalidación de un módulo se propaga a todos sus dependientes en cadena.CACHE_VERSION, toda la caché existente se invalida: cada entrada almacena la versión con la que fue creada y get la compara.