Dependency graph for FORD

I’ve recently started using FORD for documentation (a really beautiful tool btw!) and stumbled on @ivanpribec’s fpm-deps for generating dependency graphs for fpm packages (this will be handy for some larger models I’m working on, so my thanks in advance!). Given that FORD is growing closer to fpm with recent changes (being able to specify FORD-interpreted project metadata in your fpm.toml), and that dependency graphs can be very useful for documentation, I wonder how easy/challenging it would be to connect the 2/make a dependency graph generator a standard FORD feature.

4 Likes

fpm-deps is in a usable state already, but admittedly I’ve only tested it on itself. Moreover, not all features are implemented yet. I am still missing the options to show,

  • built-in fpm dependencies
  • dev-dependencies
  • exclude dependencies by name (edit: added in commit b4a1e81)

I’m begun work on the last item. The first two items will be harder, because I need to familiarize myself with fpm internals. Also the color/style of nodes and edges may still be subject to change. Please free welcome to help! I’m hoping to make a release soon.

Given that FORD is written in Python, to use fpm-deps you would either need to run it as a Python subprocess to print the graph (either as formatted text or image) or create a Python extension module (not part of my plans currently).

4 Likes

There is now an option to generate an interactive HTML page:

The clickable links refer to the homepage (mandatory) and description field (optional) in your fpm manifest file.

5 Likes

Love it. I’ll check it out more after work today.

1 Like

It’s very interesting to look at how the package manager enables composition. For instance if we look at one of @fedebenelli’s packages: GitHub - ipqa-research/fenvelopes: Program to calculate phase boundaries of multicomponent systems using Equations of State. WIP now suporting PT envelopes and PX envelopes with partial three-phase-behaviour

7 Likes

Neat idea! Feel free to make an issue on Ford – PRs are always welcome too! :slight_smile:

2 Likes

@ivanpribec This is amazing! Great job! I wonder if we could do something like scrape GitHub for all the fpm.toml files and get a single diagram like this that includes maybe the top 10 or 20 projects with the most dependencies. Just as a sort of showcase? It would be interesting to see.

FPM is just a game changer. To think, 5 years ago we didn’t have anything like this, and now look at what is possible. We have really entered a new era for Fortran.

5 Likes

Fortran-packages-list is obtained from Fortran-code-on-GitHub by using the xfpm.py script at the former repo to check for the existence of an fpm.toml file in each repo in the second list. It could be changed to save fpm.toml files for further analysis.

1 Like

Wow, this is really nice!

I could look into making a python wrapper for your fpm-deps, if you are interested in that

1 Like

Thanks! I’ve fixed a couple bugs meanwhile. There is also a new fpm-tree command available:

~/fortran/fpmdeps$ fpm tree
fpm-deps
└── fpm
    ├── toml-f
    ├── M_CLI2
    ├── fortran-regex
    ├── jonquil
    │   └── toml-f (*)
    └── fortran-shlex

I’m not familiar with FORD, but I think a solution like follows (generated by passing the help string to ChatGPT), would be sufficient:

import subprocess
from typing import Optional, List

def run_fpm_deps(
    output: str,
    manifest_path: Optional[str] = None,
    depth: Optional[int] = None,
    exclude: Optional[List[str]] = None,
    mermaid: Optional[str] = None,
    dpi: Optional[int] = None,
    no_url: bool = False,
    no_tooltip: bool = False,
    rankdir: Optional[str] = "TB",
) -> None:
    """
    Run the fpm-deps command with the given options.

    Parameters:
        output (str): Output file name.
        manifest_path (str): Path to the fpm TOML file.
        depth (int): Dependency depth to consider.
        exclude (List[str]): List of packages to exclude from the graph.
        mermaid (str): Format for mermaid output ("md" or "html").
        dpi (int): Dots per inch for bitmap output.
        no_url (bool): If True, do not add URLs to nodes.
        no_tooltip (bool): If True, do not add tooltips to nodes.
        rankdir (str): Direction of graph layout ("TB", "BT", "LR", "RL").

    Returns:
        None
    """

    command = ["fpm-deps"]

    if output:
        command += ["-o", output]
    if manifest_path:
        command += ["--manifest-path", manifest_path]
    if depth is not None:
        command += ["--depth", str(depth)]
    if exclude:
        command += ["--exclude", ",".join(exclude)]
    if mermaid:
        command += ["--mermaid", mermaid]
    if dpi:
        command += ["--dpi", str(dpi)]
    if no_url:
        command.append("--no-url")
    if no_tooltip:
        command.append("--no-tooltip")
    if rankdir:
        command += ["--rankdir", rankdir]

    try:
        subprocess.run(command, check=True)
    except subprocess.CalledProcessError as e:
        print(f"Error: fpm-deps command failed with exit code {e.returncode}")
    except FileNotFoundError:
        print("Error: fpm-deps command not found. Make sure it is installed and in your PATH.")


if __name__ == "__main__":
    run_fpm_deps("test.gv")

1 Like