Draw dependencies between physical folders in a Rust project. Export GraphML for yEd, Mermaid, or DOT, highlight cycles, and check cycles or outgoing dependency limits between folders or files.
Install the analysis components, then install the latest published release:
rustup component add rust-analyzer rust-src
cargo install source-mesh --lockedBefore the first crates.io release, install directly from GitHub:
cargo install --git https://github.com/GeneralGDA/source-mesh --locked source-meshCargo installs an optimized release build. Keep Cargo and rust-analyzer on PATH; install rust-analyzer and rust-src for the toolchain selected by the project you analyze. source-mesh checks both prerequisites before starting analysis.
From the Rust project you want to analyze:
source-mesh . --root src --format graphml --exclude-tests --output folders.graphmlOpen folders.graphml in yEd and apply a layout. To find and highlight cycles:
source-mesh . --root src --format graphml --exclude-tests --highlight-cycles --cycles-output folder-cycles.md --file-cycles-output file-cycles.md --output folders.graphml| Option | Effect |
|---|---|
--depth 1 |
Only folders immediately below --root (default: all depths) |
--group-folders |
Show subfolders inside their parent groups |
--format dot / --format mermaid |
Export another diagram format |
--exclude-tests |
Omit dependencies found only with test configuration enabled |
--highlight-cycles |
Give each connected group of cycles a distinct color |
--fail-on-cycles |
Exit unsuccessfully when file or folder cycle groups are found, after writing requested outputs |
--cycles-output FILE |
Write folder cycle chains to Markdown |
--file-cycles-output FILE |
Write file cycle chains before folder aggregation |
--max-outgoing-dependencies N |
Fail when a file or folder has more than N distinct outgoing targets |
--outgoing-dependencies-mode files|folders |
Select the threshold check's scope (default: folders) |
--outgoing-dependencies-output FILE |
Write outgoing dependency violations to Markdown; requires a threshold |
--verbose / -v |
Include diagnostic details and timings |
A → B means a file in A refers to a symbol defined in B. Edge labels count distinct file pairs. Folder aggregation can introduce cycles even when the file graph is acyclic.
Analysis of large projects can take several minutes. Use --verbose for diagnostic details and source-mesh --help for all options.
Limit each folder to at most five distinct dependency targets:
source-mesh . --root src --exclude-tests --max-outgoing-dependencies 5 --outgoing-dependencies-output dependencies.md --output folders.dotTo check individual files instead, add --outgoing-dependencies-mode files. The default mode, folders, checks the final
folder graph after --root filtering and --depth folding. File mode checks the file graph before folder aggregation, so
folder depth does not affect its counts. Both modes respect --root and --exclude-tests; targets outside the root are excluded.
--group-folders only changes diagram presentation, not dependency counts.
Each distinct target counts once, even if referenced repeatedly or by both production and test code. Folder edge weights
(the number of file pairs) are not summed. A retained folder self-edge counts as one target when --include-self is enabled.
N must be a non-negative integer: equality passes, and 0 forbids any outgoing dependencies. Without a threshold, no check runs;
an explicit mode or report path requires --max-outgoing-dependencies.
On a violation, source-mesh writes the diagram and requested reports, then exits with code 1. Stderr lists every violating
file or folder with its count and the threshold. The optional Markdown report lists the same violations, or explicitly states
that none were found. When combined with --fail-on-cycles, diagnostics include both kinds of failure.
--fail-on-cycles turns file or folder dependency cycles into a failing CI step. The diagram and requested reports are written before the failure, so the artifact is available for diagnosis. Adjust --root src for the source layout of the repository being checked.
jobs:
dependency-cycles:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- run: rustup component add rust-analyzer rust-src
- uses: Swatinem/rust-cache@v2.9.2
- name: Install source-mesh
run: cargo install --git https://github.com/GeneralGDA/source-mesh --locked source-mesh
- name: Check dependency cycles
run: |
mkdir -p target/source-mesh
source-mesh . --root src --format graphml --exclude-tests --highlight-cycles --fail-on-cycles --cycles-output target/source-mesh/folder-cycles.md --file-cycles-output target/source-mesh/file-cycles.md --output target/source-mesh/folder-dependencies.graphml
- name: Upload cycle diagnostics
if: ${{ failure() }}
uses: actions/upload-artifact@v7.0.1
with:
name: source-mesh-cycle-diagnostics
path: target/source-mesh
if-no-files-found: warnSave analysis with --save-scip analysis.scip; reuse it with --from-scip analysis.scip to change formats, depth, or filters without analyzing the source again.
For contributor documentation, see the development guide.
MIT.