gidd turns GCC/G++ -H include traces or captured compiler invocations into dependency diagrams for C and C++ projects. It parses include nesting, keeps graph nodes keyed by normalized paths, applies optional path-prefix filters, and writes Graphviz DOT, PlantUML, JSON, and a self-contained interactive HTML viewer.
cmake -S . -B build
cmake --build build
ctest --test-dir buildThe default build always produces gidd and gidd-capture. If CMake can find
LLVM and Clang development packages, gidd-capture is built with the
Clang-backed scanner; otherwise it still passes compiler invocations through
but records scan failures.
The current command reads input.txt and optional filter.txt from the working directory, then writes clustered and unclustered diagrams plus JSON and HTML viewer reports with the output prefix:
./gidd./gidd --capture-dir <dir> is the only supported command-line option. Other
input paths, output prefixes, selected formats, and cluster modes are currently
fixed in the entry point.
Generated files:
output.dot
output_no_clusters.dot
output.puml
output_no_clusters.puml
output.json
output.html
To capture an include trace from a CMake project, add -H to the compiler flags used by the target you want to inspect, then capture a single-threaded rebuild:
cmake --build build --clean-first -- -j1 > input.txt 2>&1
./giddWhen built with LLVM/Clang development libraries, gidd-capture can be used as
a transparent compiler wrapper. It runs the real compiler first, then records
nested include dependencies for successful compile commands under
GIDD_TRACE_DIR.
For CMake, prefer compiler launcher mode:
cmake -S . -B build \
-DCMAKE_C_COMPILER_LAUNCHER=/path/to/gidd-capture \
-DCMAKE_CXX_COMPILER_LAUNCHER=/path/to/gidd-capture
GIDD_TRACE_DIR=$PWD/build/gidd-trace cmake --build build
./gidd --capture-dir build/gidd-tracegidd-capture can also be used as CC or CXX when the real compiler is
provided explicitly:
GIDD_REAL_CXX=clang++ \
CXX=/path/to/gidd-capture \
cmake -S . -B build
GIDD_TRACE_DIR=$PWD/build/gidd-trace \
GIDD_REAL_CXX=clang++ \
cmake --build buildIf the real compiler succeeds but Clang cannot replay the command for scanning, the build still succeeds and the capture record contains the scan diagnostic.
Create filter.txt next to input.txt with one normalized path prefix per line:
/usr/include
/opt/vendor
Any dependency is omitted when either endpoint starts with a filter prefix.
This example is simplified from the tests:
std::stringstream input;
input << "[ 50%] Building CXX object CMakeFiles/app.dir/src/app.cpp.o\n";
input << ". /project/include/app.h\n";
input << ".. /project/include/detail/config.h\n";
gidd::IncludeGraph graph = gidd::parseGccIncludeTrace(input);
assert(graph.roots.count("src/app.cpp") == 1);
assert(graph.dependencies.count({"src/app.cpp", "/project/include/app.h"}) == 1);
assert(graph.dependencies.count({"/project/include/app.h",
"/project/include/detail/config.h"}) == 1);
struct IncludeGraph { std::set<std::pair<std::string, std::string>> dependencies; std::set<std::string> roots; };
In-memory dependency graph. dependencies stores directed edges between normalized path node ids; roots stores translation-unit roots parsed from compiler object lines.
IncludeGraph parseGccIncludeTrace(std::istream &input);
Parses GCC/G++ -H include trace lines and CMake Building CXX object / Building C object translation-unit markers into an IncludeGraph.
std::string normalizePath(std::string path);
Lexically normalizes a path without touching the filesystem. It converts separators to /, collapses repeated separators and ., and resolves safe .. segments.
std::set<std::string> graphNodes(const IncludeGraph &graph);
Returns every node mentioned by graph roots or dependency endpoints.
std::map<std::string, std::set<std::string>> nodesByDirectory(const IncludeGraph &graph);
Groups graph nodes by the directory portion of their normalized node id.
std::map<std::string, std::string> displayLabels(const IncludeGraph &graph);
Returns readable labels for diagram nodes. Unique basenames use the basename; duplicate basenames include the minimal parent path needed to disambiguate.
bool dependencyMatchesFilter(const std::pair<std::string, std::string> &dependency, const std::vector<std::string> &filters);
Returns true when either dependency endpoint starts with a normalized filter prefix.
output.html embeds the filtered include graph and opens directly in a browser.
It starts from translation-unit roots, lets you search files by path, expand
direct or reverse include relationships, fold selected subgraphs, pan/zoom the
graph, hide multiple path prefixes such as /usr/include or a specific header,
highlight full upstream/downstream paths for a selected file, collapse either
side panel to focus on the graph, and inspect incoming/outgoing includes.
Browser-side path filters hide directly matched files and folders, then keep
shared downstream headers visible when they are still reachable from an
unfiltered source.
output.json contains the same filtered graph data for other tools:
{
"nodes": [{"id": "src/app.cpp", "label": "app.cpp", "directory": "src", "kind": "root", "root": true}],
"edges": [{"from": "src/app.cpp", "to": "/project/include/app.h"}],
"roots": ["src/app.cpp"]
}When GIDD_TRACE_DIR is set, gidd-capture writes one JSON record per
successful real compiler invocation. Record filenames are derived from the
working directory, compiler arguments, and process id, and are not intended as a
stable public API.
{
"working_directory": "/tmp/build",
"arguments": ["-Iinclude", "src/app.cpp"],
"translation_unit": "src/app.cpp",
"scan_succeeded": true,
"diagnostics": [],
"dependencies": [
{"from": "src/app.cpp", "to": "/project/include/app.h"}
]
}./gidd --capture-dir <dir> reads .json files in that directory, skips
malformed records with a stderr diagnostic, skips records where
scan_succeeded is false, and merges the remaining dependencies into the same
writer pipeline used for trace input.
- Missing
input.txtis treated as an empty trace and still writes empty outputs. - Missing
filter.txtmeans no filters are applied. - Trace parsing is permissive: unknown non-trace lines are ignored.
gidd-capturereturns the real compiler's non-zero exit status and does not write a capture record for failed compiles.- If the real compiler succeeds but include scanning fails, the wrapper still
returns success and writes a record with diagnostics when
GIDD_TRACE_DIRis set. giddreturns non-zero when any requested output file cannot be written.
const std::vector<OutputFormatInfo> &supportedOutputFormats();
Returns the registered diagram/report formats. The current formats are dot,
puml, json, and html.
bool parseOutputFormat(const std::string &name, OutputFormat *format);
Maps a format registry name to an OutputFormat.
std::vector<OutputFormat> defaultOutputFormats();
Returns the formats emitted by the current CLI entry point: DOT, PlantUML, JSON, and HTML.
bool writeDiagram(OutputFormat format, const std::string &output_prefix, const std::vector<std::string> &filters, const IncludeGraph &graph, const DiagramOptions &options);
Writes one DOT, PlantUML, JSON, or HTML diagram/report using the requested output prefix, path-prefix filters, graph, and clustering option where applicable.
Here is an example of how a tiny hello world include graph can look:
#include <cstdio>
int main() {
printf("Hello, World!\n");
}


