|
35 | 35 | 'Requirements module is not installed. Run "pip install requirements-parser"' |
36 | 36 | ) |
37 | 37 |
|
| 38 | +try: |
| 39 | + import graphlib |
| 40 | +except ImportError: |
| 41 | + sys.exit("This script requires Python 3.9 or higher for graphlib support.") |
| 42 | + |
38 | 43 | parser = argparse.ArgumentParser() |
39 | 44 | parser.add_argument("packages", nargs="*") |
40 | 45 | parser.add_argument( |
@@ -425,9 +430,10 @@ def get_flatpak_runtime_scope(runtime: str) -> str: |
425 | 430 | if not output_filename.endswith(suffix): |
426 | 431 | output_filename += suffix |
427 | 432 |
|
428 | | -modules: list[dict[str, str | list[str] | list[dict[str, Any]]]] = [] |
429 | | -vcs_modules: list[dict[str, str | list[str] | list[dict[str, Any]]]] = [] |
430 | | -sources = {} |
| 433 | +modules: list[dict[str, Any]] = [] |
| 434 | +vcs_modules: list[dict[str, Any]] = [] |
| 435 | +sources: dict[str, Any] = {} |
| 436 | +dependency_graph: dict[str, set[str]] = {} |
431 | 437 |
|
432 | 438 | unresolved_dependencies_errors = [] |
433 | 439 |
|
@@ -558,6 +564,10 @@ def get_flatpak_runtime_scope(runtime: str) -> str: |
558 | 564 | ] |
559 | 565 |
|
560 | 566 | fprint("Generating dependencies") |
| 567 | + |
| 568 | +# Build dependency graph |
| 569 | +modules_map: dict[str, dict[str, Any]] = {} |
| 570 | + |
561 | 571 | for package in packages: |
562 | 572 | if package.name is None: |
563 | 573 | print( |
@@ -625,8 +635,18 @@ def get_flatpak_runtime_scope(runtime: str) -> str: |
625 | 635 |
|
626 | 636 | is_vcs = bool(package.vcs) |
627 | 637 | package_sources = [] |
| 638 | + |
| 639 | + # Add to dependency graph |
| 640 | + package_name_key = package.name.casefold() |
| 641 | + dependency_graph[package_name_key] = set() |
| 642 | + |
628 | 643 | for dependency in dependencies: |
629 | 644 | casefolded = dependency.casefold() |
| 645 | + |
| 646 | + # Add dependency to graph (ignore self-dependencies) |
| 647 | + if casefolded != package_name_key: |
| 648 | + dependency_graph[package_name_key].add(casefolded) |
| 649 | + |
630 | 650 | if casefolded in sources and sources[casefolded].get("pypi") is True: |
631 | 651 | source = sources[casefolded] |
632 | 652 | elif dependency in sources and sources[dependency].get("pypi") is False: |
@@ -684,6 +704,26 @@ def get_flatpak_runtime_scope(runtime: str) -> str: |
684 | 704 | if package.vcs: |
685 | 705 | vcs_modules.append(module) |
686 | 706 | else: |
| 707 | + # Store module for sorting later |
| 708 | + modules_map[package_name_key] = module |
| 709 | + |
| 710 | +# Topological Sort |
| 711 | +ts = graphlib.TopologicalSorter(dependency_graph) |
| 712 | +try: |
| 713 | + sorted_nodes = list(ts.static_order()) |
| 714 | +except graphlib.CycleError as cycle_error: |
| 715 | + print(f"Dependency cycle detected: {cycle_error}. Falling back to input order.") |
| 716 | + sorted_nodes = list(modules_map.keys()) |
| 717 | + |
| 718 | +# Reconstruct modules list based on sorted order |
| 719 | +# Only include modules that were actually processed (present in modules_map) |
| 720 | +for node in sorted_nodes: |
| 721 | + if node in modules_map: |
| 722 | + modules.append(modules_map[node]) |
| 723 | + |
| 724 | +# Add any modules that might have been missed |
| 725 | +for name, module in modules_map.items(): |
| 726 | + if module not in modules: |
687 | 727 | modules.append(module) |
688 | 728 |
|
689 | 729 | modules = vcs_modules + modules |
|
0 commit comments