Split a list of ready SCCs into stale and fresh. Fresh SCCs are those where: * We have valid cache files for all modules in the SCC. * There are no changes in dependencies (files removed from/added to the build). * The interface hashes of dependencies matches those recorded in the c
(
sccs: list[SCC], graph: Graph, manager: BuildManager
)
| 4471 | |
| 4472 | |
| 4473 | def find_stale_sccs( |
| 4474 | sccs: list[SCC], graph: Graph, manager: BuildManager |
| 4475 | ) -> tuple[list[SCC], list[SCC]]: |
| 4476 | """Split a list of ready SCCs into stale and fresh. |
| 4477 | |
| 4478 | Fresh SCCs are those where: |
| 4479 | * We have valid cache files for all modules in the SCC. |
| 4480 | * There are no changes in dependencies (files removed from/added to the build). |
| 4481 | * The interface hashes of dependencies matches those recorded in the cache. |
| 4482 | * All indirect dependencies are still reachable via direct ones. |
| 4483 | The first and second conditions are verified by is_fresh(). |
| 4484 | """ |
| 4485 | stale_sccs = [] |
| 4486 | fresh_sccs = [] |
| 4487 | for ascc in sccs: |
| 4488 | stale_scc = {id for id in ascc.mod_ids if not graph[id].is_fresh()} |
| 4489 | fresh = not stale_scc |
| 4490 | |
| 4491 | # Verify that interfaces of dependencies still present in graph are up-to-date (fresh). |
| 4492 | stale_deps = set() |
| 4493 | for id in ascc.mod_ids: |
| 4494 | for dep in graph[id].dep_hashes: |
| 4495 | if dep in graph and graph[dep].interface_hash != graph[id].dep_hashes[dep]: |
| 4496 | stale_deps.add(dep) |
| 4497 | fresh = fresh and not stale_deps |
| 4498 | |
| 4499 | # Verify the invariant that indirect dependencies are a subset of transitive direct |
| 4500 | # dependencies. Note: the case where indirect dependency is removed from the graph |
| 4501 | # completely is already handled above. |
| 4502 | stale_indirect = None |
| 4503 | if fresh: |
| 4504 | stale_indirect = verify_transitive_deps(ascc, graph, manager) |
| 4505 | if stale_indirect is not None: |
| 4506 | fresh = False |
| 4507 | |
| 4508 | if manager.logging_enabled: |
| 4509 | if fresh: |
| 4510 | fresh_msg = "fresh" |
| 4511 | elif stale_scc: |
| 4512 | fresh_msg = "inherently stale" |
| 4513 | if stale_scc != ascc.mod_ids: |
| 4514 | fresh_msg += f" ({' '.join(sorted(stale_scc))})" |
| 4515 | if stale_deps: |
| 4516 | fresh_msg += f" with stale deps ({' '.join(sorted(stale_deps))})" |
| 4517 | elif stale_deps: |
| 4518 | fresh_msg = f"stale due to deps ({' '.join(sorted(stale_deps))})" |
| 4519 | else: |
| 4520 | assert stale_indirect is not None |
| 4521 | fresh_msg = f"stale due to stale indirect dep(s): first {stale_indirect}" |
| 4522 | scc_str = " ".join(ascc.mod_ids) |
| 4523 | |
| 4524 | if fresh: |
| 4525 | if manager.tracing_enabled: |
| 4526 | manager.trace(f"Found {fresh_msg} SCC ({scc_str})") |
| 4527 | # If there is at most one file with errors we can skip the ordering to save time. |
| 4528 | mods_with_errors = [id for id in ascc.mod_ids if graph[id].error_lines] |
| 4529 | if len(mods_with_errors) <= 1: |
| 4530 | scc = mods_with_errors |
no test coverage detected
searching dependent graphs…