Files
build_infra/Graphviz/Project_Graphs.sh.in
T
2026-07-28 10:54:38 +08:00

157 lines
6.4 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
inputs=(@PSC_INPUT_DIRS_SH@)
output_dir='@PSC_OUTPUT_DIR@'
format='@PSC_FORMAT@'
dot_exe='@PSC_DOT_EXECUTABLE@'
max_depth=@PSC_MAX_DEPTH@
collapse_threshold=@PSC_COLLAPSE_THRESHOLD@
mkdir -p "$output_dir"
normalize_path() {
local path="$1"
if command -v realpath >/dev/null 2>&1; then
realpath -m "$path" | tr '\\' '/'
else
printf '%s\n' "$path" | tr '\\' '/'
fi
}
dot_text() {
printf '%s' "$1" | sed 's/\\/\\//g; s/"/\\"/g'
}
root_name() {
local path
path=$(normalize_path "$1")
case "$path" in
*/module/radio) printf 'radio' ;;
*/CPP_Core/Core) printf 'CPP_Core' ;;
*/YSGraphic_Core) printf 'YSGraphic_Core' ;;
*) basename "$path" ;;
esac
}
write_header() {
local title="$1"
echo "digraph \"$title\" {"
echo 'rankdir=LR;'
echo 'splines=ortho;'
echo 'concentrate=true;'
echo 'forcelabels=true;'
echo "graph [fontname=\"Consolas\", label=\"$title\", labelloc=t, fontsize=18];"
echo 'node [shape=box, style="rounded,filled", fontname="Consolas", fontsize=10];'
echo 'edge [fontname="Consolas", fontsize=9];'
}
write_legend() {
echo 'subgraph cluster_legend {'
echo 'label="legend";'
echo 'style="rounded,dashed";'
echo '"legend_module" [label="module/folder", fillcolor="#dbeafe"];'
echo '"legend_target" [label="cmake target", fillcolor="#dcfce7"];'
echo '"legend_buffer" [label="buffer/cache", fillcolor="#fef3c7"];'
echo '"legend_runtime" [label="runtime", fillcolor="#fee2e2"];'
echo '"legend_edge" [label="edge label = relation/count", fillcolor="#f8fafc"];'
echo '}'
}
render_dot() {
local name="$1"
"$dot_exe" "-T$format" "$output_dir/$name.dot" -o "$output_dir/$name.$format"
}
write_graphviz_index() {
local items=(radio_directory_tree radio_module_dependency radio_cmake_targets radio_include_dependency radio_render_architecture)
{
echo '<!doctype html><meta charset="utf-8"><title>Radio Graphviz</title><h1>Radio Graphviz</h1><ul>'
for item in "${items[@]}"; do
local file="$item.$format"
if [ -f "$output_dir/$file" ]; then
echo "<li><a href=\"$file\">$item</a></li>"
else
echo "<li>$item (not generated)</li>"
fi
done
echo '</ul>'
} > "$output_dir/index.html"
}
write_directory_tree() {
local dot="$output_dir/radio_directory_tree.dot"
{
write_header 'radio directory_tree'
for root in "${inputs[@]}"; do
[ -d "$root" ] || continue
local root_label
root_label=$(root_name "$root")
echo "subgraph \"cluster_$root_label\" {"
echo "label=\"$root_label\";"
echo 'style="rounded";'
find "$root" -maxdepth "$max_depth" -type d | while read -r dir; do
local rel name id files dirs parent parent_id
rel=${dir#"$root"}
rel=${rel#/}
name=$(basename "$dir")
[ -z "$rel" ] && name="$root_label"
id="$root_label${rel:+/$rel}"
files=$(find "$dir" -maxdepth 1 -type f \( -name '*.h' -o -name '*.hpp' -o -name '*.hh' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.cc' \) | wc -l)
dirs=$(find "$dir" -maxdepth 1 -mindepth 1 -type d | wc -l)
echo "\"dir:$id\" [label=\"$(dot_text "$name")\\nfiles: $files\\ndirs: $dirs\", tooltip=\"$(dot_text "$id")\", fillcolor=\"#dbeafe\"];"
if [ -n "$rel" ]; then
parent=$(dirname "$rel")
if [ "$parent" = "." ]; then
parent_id="$root_label"
else
parent_id="$root_label/$parent"
fi
echo "\"dir:$parent_id\" -> \"dir:$id\" [xlabel=\"contains\"];"
fi
done
echo '}'
done
write_legend
echo '}'
} > "$dot"
render_dot 'radio_directory_tree'
}
write_empty_dependency() {
local name="$1"
local title="$2"
{
write_header "$title"
echo '"module_dependency_note" [label="include dependency aggregation is generated by the PowerShell script on Windows", fillcolor="#dbeafe"];'
write_legend
echo '}'
} > "$output_dir/$name.dot"
render_dot "$name"
}
write_render_architecture() {
{
write_header 'radio render_architecture'
echo '"Plot" [label="Plot", fillcolor="#dbeafe"];'
echo '"Renderable" [label="Renderable", fillcolor="#dbeafe"];'
echo '"Render_Data" [label="Render_Data", fillcolor="#dcfce7"];'
echo '"Render_State" [label="Render_State", fillcolor="#dcfce7"];'
echo '"Input_Data" [label="Input_Data", fillcolor="#dcfce7"];'
echo '"Render_Pipeline" [label="Render_Pipeline", fillcolor="#dcfce7"];'
echo '"State_Buffer" [label="State_Buffer[3]", fillcolor="#fef3c7"];'
echo '"Input_Buffer" [label="Input_Buffer[3]", fillcolor="#fef3c7"];'
echo '"Render_Cache" [label="Render_Cache", fillcolor="#fef3c7"];'
echo '"Color_Buffer" [label="Color_Buffer[3]", fillcolor="#fef3c7"];'
echo '"CPU_Render_Task" [label="CPU Render Task", fillcolor="#fee2e2"];'
echo '"Qt_paintEvent" [label="Qt paintEvent", fillcolor="#fee2e2"];'
echo '"Plot" -> "Renderable" [xlabel="owns/schedules"];'
echo '"Renderable" -> "Render_Data" [xlabel="owns"];'
echo '"Render_Data" -> "Render_State" [xlabel="state snapshot"];'
echo '"Render_Data" -> "Input_Data" [xlabel="input batch"];'
echo '"Render_Data" -> "Render_Cache" [xlabel="long-lived cache"];'
echo '"Renderable" -> "Render_Pipeline" [xlabel="submit/finish/publish"];'
echo '"Render_Pipeline" -> "State_Buffer" [xlabel="edit/ready/render"];'
echo '"Render_Pipeline" -> "Input_Buffer" [xlabel="write/ready/render"];'
echo '"Render_Pipeline" -> "CPU_Render_Task" [xlabel="dispatch"];'
echo '"CPU_Render_Task" -> "Color_Buffer" [xlabel="write render color"];'
echo '"Color_Buffer" -> "Qt_paintEvent" [xlabel="ready/front consume"];'
write_legend
echo '}'
} > "$output_dir/radio_render_architecture.dot"
render_dot 'radio_render_architecture'
}
write_directory_tree
write_empty_dependency 'radio_module_dependency' 'radio module_dependency'
write_empty_dependency 'radio_include_dependency' 'radio include_dependency'
write_render_architecture
write_graphviz_index