Manages binary fingerprints for fastled shared library and test DLLs to suppress unnecessary relinking when library content hasn't changed. Thread-safe: touch_dlls_if_lib_unchanged() is designed to be called from a background thread while ninja continues building.
| 64 | |
| 65 | |
| 66 | class BuildOptimizer: |
| 67 | """ |
| 68 | Manages binary fingerprints for fastled shared library and test DLLs to suppress |
| 69 | unnecessary relinking when library content hasn't changed. |
| 70 | |
| 71 | Thread-safe: touch_dlls_if_lib_unchanged() is designed to be called from |
| 72 | a background thread while ninja continues building. |
| 73 | """ |
| 74 | |
| 75 | def __init__(self, cache_file: Path) -> None: |
| 76 | self._cache_file = cache_file |
| 77 | self._saved: dict = {} |
| 78 | self._load() |
| 79 | |
| 80 | def _load(self) -> None: |
| 81 | """Load saved fingerprints from the cache file.""" |
| 82 | if not self._cache_file.exists(): |
| 83 | return |
| 84 | try: |
| 85 | with open(self._cache_file, "r", encoding="utf-8") as f: |
| 86 | data = json.load(f) |
| 87 | if isinstance(data, dict): |
| 88 | self._saved = data |
| 89 | except (json.JSONDecodeError, OSError): |
| 90 | self._saved = {} |
| 91 | |
| 92 | def touch_dlls_if_lib_unchanged(self, build_dir: Path) -> int: |
| 93 | """ |
| 94 | Check if fastled shared library content matches the saved fingerprint. |
| 95 | If so, touch all DLL files whose content also matches the saved fingerprint, |
| 96 | making their mtime newer than fastled shared library so ninja skips relinking them. |
| 97 | |
| 98 | This is called immediately after the "Linking static target fastled shared library" |
| 99 | line appears in ninja output - at that point fastled shared library has been freshly |
| 100 | archived and its content can be compared to the saved fingerprint. |
| 101 | |
| 102 | Returns: number of DLLs touched (had mtime updated) |
| 103 | """ |
| 104 | lib_path = build_dir / _LIBFASTLED_REL_PATH |
| 105 | if not lib_path.exists(): |
| 106 | return 0 |
| 107 | |
| 108 | saved_lib_hash = self._saved.get("libfastled_hash", "") |
| 109 | if not saved_lib_hash: |
| 110 | return 0 |
| 111 | |
| 112 | current_lib_hash = _hash_file(lib_path) |
| 113 | if not current_lib_hash or current_lib_hash != saved_lib_hash: |
| 114 | # Library content changed - let ninja relink normally |
| 115 | return 0 |
| 116 | |
| 117 | # Library content unchanged - touch DLLs whose content also matches |
| 118 | saved_dll_hashes: dict[str, str] = self._saved.get("dll_hashes", {}) |
| 119 | if not saved_dll_hashes: |
| 120 | return 0 |
| 121 | |
| 122 | now = time.time() |
| 123 | touched = 0 |
no outgoing calls
no test coverage detected