Determine if a source file has changed since the last known modification time. Two-layer verification process: 1. Fast modification time check - if times match, return False immediately 2. Content verification - compute/use cached MD5 hash for accurate comparison
(self, src_path: Path, previous_modtime: float)
| 144 | raise IOError(f"Cannot read file {file_path}: {e}") from e |
| 145 | |
| 146 | def has_changed(self, src_path: Path, previous_modtime: float) -> bool: |
| 147 | """ |
| 148 | Determine if a source file has changed since the last known modification time. |
| 149 | |
| 150 | Two-layer verification process: |
| 151 | 1. Fast modification time check - if times match, return False immediately |
| 152 | 2. Content verification - compute/use cached MD5 hash for accurate comparison |
| 153 | |
| 154 | Args: |
| 155 | src_path: Path to the source file to check |
| 156 | previous_modtime: Previously known modification time (Unix timestamp) |
| 157 | |
| 158 | Returns: |
| 159 | True if the file has changed, False if unchanged |
| 160 | |
| 161 | Raises: |
| 162 | FileNotFoundError: If source file doesn't exist |
| 163 | """ |
| 164 | if not src_path.exists(): |
| 165 | raise FileNotFoundError(f"Source file not found: {src_path}") |
| 166 | |
| 167 | current_modtime = os.path.getmtime(src_path) |
| 168 | |
| 169 | # Optional mode: strictly use modification time only (no hashing) |
| 170 | # This is required for toolchains that invalidate PCH on any newer |
| 171 | # dependency regardless of content changes (e.g., Clang). |
| 172 | if self._modtime_only: |
| 173 | # Treat as changed only when file is newer than the reference time |
| 174 | # (keeps behavior stable when reference is an external artifact's mtime) |
| 175 | return current_modtime > previous_modtime |
| 176 | |
| 177 | # Layer 1: Quick modification time check |
| 178 | if current_modtime == previous_modtime: |
| 179 | return False # No change detected |
| 180 | |
| 181 | file_key = str(src_path.resolve()) # Use absolute path as key |
| 182 | |
| 183 | # Layer 2: Content verification via hash comparison |
| 184 | if file_key in self.cache: |
| 185 | cached_entry = self.cache[file_key] |
| 186 | previous_hash = ( |
| 187 | cached_entry.md5_hash |
| 188 | ) # Store previous hash before potential update |
| 189 | |
| 190 | if current_modtime == cached_entry.modification_time: |
| 191 | # File is cached with current modtime - use cached hash |
| 192 | current_hash = cached_entry.md5_hash |
| 193 | else: |
| 194 | # Cache is stale - compute new hash |
| 195 | current_hash = self._compute_md5(src_path) |
| 196 | self._update_cache_entry(src_path, current_modtime, current_hash) |
| 197 | |
| 198 | # Compare current hash with previous cached hash |
| 199 | # If they match, content hasn't actually changed despite modtime difference |
| 200 | return current_hash != previous_hash |
| 201 | |
| 202 | else: |
| 203 | # File not in cache - compute hash and cache it |