Return max mtime of any source file under root, skipping build directories. Uses os.scandir() for efficiency. On Windows, DirEntry.stat() uses cached metadata from FindNextFile (no extra syscall per file). On Linux, scandir similarly batches directory reads. Detects BOTH structural
(
root: Path, exts: Optional[frozenset[str]] = None
)
| 96 | |
| 97 | |
| 98 | def _get_max_source_file_mtime( |
| 99 | root: Path, exts: Optional[frozenset[str]] = None |
| 100 | ) -> float: |
| 101 | """Return max mtime of any source file under root, skipping build directories. |
| 102 | |
| 103 | Uses os.scandir() for efficiency. On Windows, DirEntry.stat() uses cached |
| 104 | metadata from FindNextFile (no extra syscall per file). On Linux, scandir |
| 105 | similarly batches directory reads. |
| 106 | |
| 107 | Detects BOTH structural changes (file add/remove) AND content modifications |
| 108 | (file writes), unlike the directory-mtime-only approach. |
| 109 | |
| 110 | Args: |
| 111 | root: Root directory to scan |
| 112 | exts: Set of file extensions to check (default: _SOURCE_EXTS for C++ files). |
| 113 | Pass _PY_SOURCE_EXTS to scan Python files instead. |
| 114 | |
| 115 | Returns 0.0 on missing root or any OS error. |
| 116 | """ |
| 117 | if exts is None: |
| 118 | exts = _SOURCE_EXTS |
| 119 | max_mtime = 0.0 |
| 120 | stack = [str(root)] |
| 121 | while stack: |
| 122 | current = stack.pop() |
| 123 | try: |
| 124 | with os.scandir(current) as it: |
| 125 | for entry in it: |
| 126 | try: |
| 127 | name = entry.name |
| 128 | if entry.is_dir(follow_symlinks=False): |
| 129 | if not _should_skip_scan_dir(name): |
| 130 | stack.append(entry.path) |
| 131 | elif entry.is_file(follow_symlinks=False): |
| 132 | _, ext = os.path.splitext(name) |
| 133 | if ext.lower() in exts: |
| 134 | mtime = entry.stat(follow_symlinks=False).st_mtime |
| 135 | if mtime > max_mtime: |
| 136 | max_mtime = mtime |
| 137 | except OSError: |
| 138 | pass |
| 139 | except OSError: |
| 140 | pass |
| 141 | return max_mtime |
| 142 | |
| 143 | |
| 144 | # --------------------------------------------------------------------------- |
no test coverage detected