Builds and visualises directed #include dependency graphs from
CMake/Ninja builds. It re-runs each compilation unit with
-H -fsyntax-only to obtain the exact include hierarchy the compiler
sees — no preprocessing guesswork — then merges all per-TU trees into
a single deduplicated graph.
Contents
Python 3.10+
uv (or pip)
A CMake/Ninja build directory containing
compile_commands.jsonEnable it in CMake with
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON, or addset(CMAKE_EXPORT_COMPILE_COMMANDS ON)to yourCMakeLists.txt.cloc(optional) — used for lines-of-code metrics in the HTML view. Install with your package manager (apt install cloc,brew install cloc, etc.).
git clone https://github.com/tobiaskaestner/dep-analyzer.git
cd dep-analyzer
uv syncAfter uv sync the dep-analyzer command is available inside the
project's virtual environment:
uv run dep-analyzer --help
Or activate the environment first:
source .venv/bin/activate dep-analyzer --help
# Open the interactive graph in your browser (builds on the fly)
dep-analyzer serve build/blinky
# Generate a self-contained HTML file instead
dep-analyzer build --html --out graph.html build/blinkyRuns the include-graph extraction and emits output in the requested format.
dep-analyzer build [BUILD_DIR] [FORMAT] [--out FILE]
[--exclude PREFIX]... [--only PREFIX]...
[-j N] [--cluster-depth N]
BUILD_DIR- Path to the Ninja build directory (the folder containing
compile_commands.json). Defaults to auto-detection: tries the current directory, then<script-dir>/build/blinky. --out FILE- Write output to FILE instead of stdout.
Output formats (mutually exclusive, default: --json):
Filtering:
--exclude PREFIX- Drop any edge where either endpoint starts with PREFIX. Repeatable. Useful for removing SDK or system headers from the graph.
--only PREFIX- Keep only edges where the source node starts with PREFIX. Repeatable. Useful to focus on a specific subsystem.
Performance:
-j N/--jobs N- Number of parallel compiler invocations (default: CPU count).
Visualisation:
--cluster-depth N- Controls how many leading path components are used to group nodes into compound clusters in the HTML view (default: 3). Increase for finer-grained grouping, decrease for a flatter layout.
Builds the graph (or loads a pre-built payload) and starts a local Flask server. Open the printed URL in any browser.
dep-analyzer serve [BUILD_DIR] [--elements FILE]
[--host HOST] [--port PORT]
[--exclude PREFIX]... [--only PREFIX]...
[-j N] [--cluster-depth N]
Accepts the same BUILD_DIR, --exclude, --only, -j, and
--cluster-depth options as build, plus:
--elements FILE- Skip the build step and load a pre-built elements JSON produced by
a previous
dep-analyzer build --htmlrun or saved payload. Useful to avoid recompiling when iterating on the visualisation. --host HOST- Network interface to bind to (default:
127.0.0.1). Use0.0.0.0to expose the server on all interfaces. --port PORT- TCP port (default:
8421).
# Live server — graph builds on the fly, open http://127.0.0.1:8421/
dep-analyzer serve build/blinky
# Self-contained HTML for sharing (no server needed)
dep-analyzer build --html --out graph.html build/blinky
# Expose the server to the local network on port 9000
dep-analyzer serve --host 0.0.0.0 --port 9000 build/blinky
# Two-phase workflow: build once, serve repeatedly without recompiling
dep-analyzer build --html --out /tmp/payload.html build/blinky
dep-analyzer serve --elements /tmp/payload.html# Only show edges that originate inside the application source tree
dep-analyzer build --html --out app-only.html \
--only /path/to/workspace/app build/blinky
# Exclude the Zephyr SDK toolchain headers
dep-analyzer build --stats \
--exclude /path/to/zephyr-sdk build/blinky
# Combine: show app sources, excluding generated files
dep-analyzer serve \
--only /path/to/workspace/app \
--exclude /path/to/workspace/build \
build/blinky# Shallow clusters (2 levels) — less nesting, faster layout
dep-analyzer serve --cluster-depth 2 build/blinky
# Deep clusters (5 levels) — mirrors directory structure closely
dep-analyzer build --html --cluster-depth 5 --out deep.html build/blinky# Limit parallelism to 4 compiler processes
dep-analyzer build --stats -j 4 build/blinky
# Use all available CPUs (default — explicit here for clarity)
dep-analyzer serve -j 0 build/blinky# JSON adjacency list to stdout
dep-analyzer build --json build/blinky
# JSON saved to a file
dep-analyzer build --json --out graph.json build/blinky
# CSV edge list
dep-analyzer build --csv --out edges.csv build/blinky
# GraphViz DOT — render to SVG
dep-analyzer build --dot --out graph.dot build/blinky
dot -Tsvg graph.dot -o graph.svg
# Per-category statistics summary
dep-analyzer build --stats build/blinky
# Cycle / SCC analysis
dep-analyzer build --cycles build/blinkyThe HTML view (both --html and serve) provides:
Graph
- Compound nodes group files by directory (depth controlled by
--cluster-depth). - Node colour encodes category (app / zephyr / modules / sdk /
generated / other) and file type (light =
.h, dark =.c/.S). - Click any file node to highlight it and its neighbours. Click again to clear. Click the background to clear.
Sidebar — Interaction panel
- grab nodes / grab compounds — toggle drag for file nodes and cluster boxes independently.
- edges interactive — when off (default), edges are invisible to the mouse so they cannot be accidentally grabbed.
- highlight incoming / highlight outgoing — choose which edge directions are shown when a node is selected.
- transitive closure — available when exactly one direction is chosen; follows edges recursively instead of showing only direct neighbours.
- max depth — limits transitive traversal to N hops (empty = unlimited).
- Space-bar hold or right-mouse-drag to pan the canvas.
Sidebar — Layout settings
All fcose layout parameters (inner/outer edge length, node repulsion, separation, iterations, tiling padding, randomise seed) are exposed as live sliders. Click Run layout to re-run with the current values.
Sidebar — Node weight
Map any per-file metric to node size:
- File size (bytes)
- Lines of code / total lines / comment lines / blank lines (via cloc)
- Incoming edges (fan-in) / outgoing edges (fan-out)
Supports linear and log scale, plus min/max clamp inputs to handle outliers without distorting the rest of the graph.
Search
Type in the search box to highlight all nodes whose filename or path contains the query string.
src/dep_analyzer/
├── cli.py — argument parsing, subcommand dispatch
├── build.py — GCC -H invocation, include-edge extraction
├── graph.py — Tarjan SCC, DAG longest path, adjacency list
├── classify.py — file categorisation, colours, edge filtering
├── metrics.py — file size and lines-of-code collection (cloc)
├── cluster.py — compound-node hierarchy builder
├── server.py — Flask dev server
├── formats/
│ ├── elements.py — Cytoscape element-list builder (shared by html + serve)
│ ├── html.py — standalone HTML output (inlines web assets)
│ ├── dot.py — GraphViz DOT
│ ├── json_.py — JSON adjacency list
│ ├── csv_.py — CSV edge list
│ ├── stats.py — per-category statistics
│ └── cycles.py — SCC / cycle report
└── web/
├── index.html — page shell (no inline content)
├── style.css — Catppuccin Mocha dark theme
└── app.js — Cytoscape.js graph, all interaction logic