Ensure all DLL outputs have mtimes newer than their key input dependencies. When zccache caches link results, it may restore outputs with old mtimes that are older than the symbols file, causing ninja to relink on every build. This function touches any such stale outputs to break t
(build_dir: Path, verbose: bool = False)
| 49 | |
| 50 | |
| 51 | def stabilize_dll_mtimes(build_dir: Path, verbose: bool = False) -> int: |
| 52 | """ |
| 53 | Ensure all DLL outputs have mtimes newer than their key input dependencies. |
| 54 | |
| 55 | When zccache caches link results, it may restore outputs with old mtimes |
| 56 | that are older than the symbols file, causing ninja to relink on every build. |
| 57 | This function touches any such stale outputs to break the relink loop. |
| 58 | |
| 59 | Args: |
| 60 | build_dir: Meson build directory (e.g., .build/meson-quick) |
| 61 | verbose: Print details about touched files |
| 62 | |
| 63 | Returns: |
| 64 | Number of files touched (0 if all mtimes were already stable) |
| 65 | """ |
| 66 | # Find the maximum mtime across all key input files |
| 67 | max_input_mtime: float = 0 |
| 68 | for rel_path in _KEY_INPUTS: |
| 69 | abs_path = build_dir / rel_path |
| 70 | if abs_path.exists(): |
| 71 | try: |
| 72 | mtime = abs_path.stat().st_mtime |
| 73 | if mtime > max_input_mtime: |
| 74 | max_input_mtime = mtime |
| 75 | except OSError: |
| 76 | pass |
| 77 | |
| 78 | if max_input_mtime == 0: |
| 79 | return 0 # No input files found, nothing to stabilize |
| 80 | |
| 81 | # Collect all DLL and runner outputs |
| 82 | all_outputs: list[Path] = [] |
| 83 | for pattern in _DLL_PATTERNS: |
| 84 | all_outputs.extend(build_dir.glob(pattern)) |
| 85 | for pattern in _RUNNER_PATTERNS: |
| 86 | candidate = build_dir / pattern |
| 87 | if candidate.exists(): |
| 88 | all_outputs.append(candidate) |
| 89 | |
| 90 | if not all_outputs: |
| 91 | return 0 |
| 92 | |
| 93 | # Touch any outputs that are older than the key inputs |
| 94 | now = time.time() |
| 95 | # Use a timestamp slightly after the max input mtime to ensure outputs |
| 96 | # are newer. Using NOW is safest since it's guaranteed to be >= max_input_mtime. |
| 97 | touch_time = max(now, max_input_mtime + 1) |
| 98 | touched = 0 |
| 99 | |
| 100 | for output_path in all_outputs: |
| 101 | try: |
| 102 | output_mtime = output_path.stat().st_mtime |
| 103 | if output_mtime <= max_input_mtime: |
| 104 | os.utime(str(output_path), (touch_time, touch_time)) |
| 105 | touched += 1 |
| 106 | except OSError: |
| 107 | pass |
| 108 |
no test coverage detected