235 lines
8.3 KiB
Python
235 lines
8.3 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
API_ROOT = SCRIPT_DIR.parent if SCRIPT_DIR.name == "_tools" else SCRIPT_DIR
|
|
REPO_ROOT = API_ROOT.parents[1]
|
|
DEFAULT_ROOT_NAME = "XCEngine"
|
|
DEFAULT_INCLUDE_ROOT = REPO_ROOT / "engine" / "include" / DEFAULT_ROOT_NAME
|
|
|
|
|
|
DESCRIPTION_MAPS = {
|
|
"XCEngine": {
|
|
"XCEngine": "与 `engine/include/XCEngine` 平行的 API 文档根目录。",
|
|
"XCEngine/Audio": "音频系统、混音器、效果器与后端接口。",
|
|
"XCEngine/Components": "组件系统与游戏对象 API。",
|
|
"XCEngine/Core": "基础类型、事件、IO、容器与数学基础设施。",
|
|
"XCEngine/Core/Asset": "资源句柄、资源管理器、异步加载与依赖图。",
|
|
"XCEngine/Core/Containers": "字符串、数组和哈希表等核心容器。",
|
|
"XCEngine/Core/IO": "资源路径、归档与虚拟文件系统。",
|
|
"XCEngine/Core/Math": "向量、矩阵、几何体与变换数学类型。",
|
|
"XCEngine/Debug": "日志、Profiler 与调试集成接口。",
|
|
"XCEngine/Input": "输入状态查询、输入事件与平台接入接口。",
|
|
"XCEngine/Memory": "分配器与内存管理。",
|
|
"XCEngine/Platform": "窗口、时钟、文件系统和平台抽象。",
|
|
"XCEngine/Platform/Windows": "Windows 平台窗口与输入接入实现。",
|
|
"XCEngine/Resources": "具体资源类型及其加载器。",
|
|
"XCEngine/Resources/AudioClip": "音频资源与音频加载器。",
|
|
"XCEngine/Resources/Material": "材质资源与材质加载器。",
|
|
"XCEngine/Resources/Mesh": "网格资源、导入设置与网格加载器。",
|
|
"XCEngine/Resources/Shader": "着色器资源与着色器加载器。",
|
|
"XCEngine/Resources/Texture": "纹理资源、导入设置与纹理加载器。",
|
|
"XCEngine/RHI": "渲染硬件抽象层及其后端。",
|
|
"XCEngine/RHI/D3D12": "DirectX 12 后端 public headers。",
|
|
"XCEngine/RHI/OpenGL": "OpenGL 后端 public headers。",
|
|
"XCEngine/Scene": "场景与场景管理器 API。",
|
|
"XCEngine/Threading": "线程封装、同步原语和任务系统。",
|
|
},
|
|
"XCEditor": {
|
|
"XCEditor": "与 `new_editor/include/XCEditor` 平行的 API 文档根目录。",
|
|
"XCEditor/Collections": "Editor 集合视图与滚动/重命名/标签条等组合控件协议。",
|
|
"XCEditor/Fields": "Editor inspector 字段控件、交互状态机与 property-grid 协议。",
|
|
"XCEditor/Foundation": "Editor 命令、快捷键与主题等基础协作契约。",
|
|
"XCEditor/Shell": "Editor 工作区、panel、dock、viewport 与 menu 壳层装配协议。",
|
|
"XCEditor/Widgets": "Editor 专用绘制/布局辅助与通用 widget primitive helper。",
|
|
},
|
|
}
|
|
|
|
NAMESPACE_MAPS = {
|
|
"XCEditor": {
|
|
"XCEditor": "XCEngine::UI::Editor",
|
|
"XCEditor/Collections": "XCEngine::UI::Editor::Widgets",
|
|
"XCEditor/Fields": "XCEngine::UI::Editor::Widgets",
|
|
"XCEditor/Foundation": "XCEngine::UI::Editor",
|
|
"XCEditor/Shell": "XCEngine::UI::Editor",
|
|
"XCEditor/Widgets": "XCEngine::UI::Editor::Widgets",
|
|
},
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class DirInfo:
|
|
include_dir: Path
|
|
doc_dir: Path
|
|
relative: str
|
|
root_name: str
|
|
namespace: str
|
|
type_label: str
|
|
description: str
|
|
|
|
|
|
def to_namespace(relative: str, root_name: str) -> str:
|
|
mapped = NAMESPACE_MAPS.get(root_name, {}).get(relative)
|
|
if mapped:
|
|
return mapped
|
|
if relative == root_name:
|
|
return root_name
|
|
return "::".join(relative.split("/"))
|
|
|
|
|
|
def get_type_label(relative: str, root_name: str) -> str:
|
|
if relative == root_name:
|
|
return "module-root"
|
|
if relative.count("/") == 1:
|
|
return "module"
|
|
return "submodule"
|
|
|
|
|
|
def get_description(relative: str, root_name: str) -> str:
|
|
return DESCRIPTION_MAPS.get(root_name, {}).get(
|
|
relative,
|
|
"与当前 public headers 平行的文档目录节点。",
|
|
)
|
|
|
|
|
|
def iter_include_dirs(
|
|
include_root: Path,
|
|
doc_root: Path,
|
|
root_name: str,
|
|
) -> list[DirInfo]:
|
|
dirs = [include_root]
|
|
dirs.extend(path for path in sorted(include_root.rglob("*")) if path.is_dir())
|
|
|
|
items: list[DirInfo] = []
|
|
for include_dir in dirs:
|
|
relative = root_name
|
|
if include_dir != include_root:
|
|
relative = root_name + "/" + include_dir.relative_to(include_root).as_posix()
|
|
|
|
doc_dir = (
|
|
doc_root
|
|
if include_dir == include_root
|
|
else doc_root / include_dir.relative_to(include_root)
|
|
)
|
|
items.append(
|
|
DirInfo(
|
|
include_dir=include_dir,
|
|
doc_dir=doc_dir,
|
|
relative=relative,
|
|
root_name=root_name,
|
|
namespace=to_namespace(relative, root_name),
|
|
type_label=get_type_label(relative, root_name),
|
|
description=get_description(relative, root_name),
|
|
)
|
|
)
|
|
return items
|
|
|
|
|
|
def relative_link(source: Path, target: Path) -> str:
|
|
return os.path.relpath(target, source.parent).replace("\\", "/")
|
|
|
|
|
|
def dir_index_name(doc_dir: Path) -> str:
|
|
return f"{doc_dir.name}.md"
|
|
|
|
|
|
def dir_index_path(doc_dir: Path) -> Path:
|
|
return doc_dir / dir_index_name(doc_dir)
|
|
|
|
|
|
def header_doc_page_name(header_name: str) -> str:
|
|
stem = Path(header_name).stem
|
|
return f"{stem}/{stem}.md"
|
|
|
|
|
|
def build_dir_index(info: DirInfo) -> str:
|
|
include_headers = sorted(path.name for path in info.include_dir.glob("*.h"))
|
|
child_dirs = sorted(path.name for path in info.include_dir.iterdir() if path.is_dir())
|
|
index_path = dir_index_path(info.doc_dir)
|
|
|
|
title = info.relative.split("/")[-1]
|
|
if info.relative == info.root_name:
|
|
title = f"{info.root_name} API 平行目录"
|
|
|
|
lines: list[str] = []
|
|
lines.append(f"# {title}")
|
|
lines.append("")
|
|
lines.append(f"**命名空间**: `{info.namespace}`")
|
|
lines.append("")
|
|
lines.append(f"**类型**: `{info.type_label}`")
|
|
lines.append("")
|
|
lines.append(f"**描述**: {info.description}")
|
|
lines.append("")
|
|
lines.append("## 概览")
|
|
lines.append("")
|
|
lines.append(
|
|
f"该目录与 `{info.relative}` 对应的 public headers 保持平行,用于承载唯一的 canonical API 文档入口。"
|
|
)
|
|
lines.append("")
|
|
|
|
if child_dirs:
|
|
lines.append("## 子目录")
|
|
lines.append("")
|
|
for child in child_dirs:
|
|
lines.append(f"- [{child}]({child}/{child}.md)")
|
|
lines.append("")
|
|
|
|
if include_headers:
|
|
lines.append("## 头文件")
|
|
lines.append("")
|
|
for header in include_headers:
|
|
page_name = header_doc_page_name(header)
|
|
page_path = info.doc_dir / Path(page_name)
|
|
if page_path.exists():
|
|
lines.append(f"- [{Path(header).stem}]({page_name}) - `{header}`")
|
|
else:
|
|
lines.append(f"- `{header}`")
|
|
lines.append("")
|
|
|
|
lines.append("## 相关文档")
|
|
lines.append("")
|
|
if info.relative == info.root_name:
|
|
lines.append("- [API 总索引](../main.md)")
|
|
lines.append("- [API 文档重构状态](../_meta/rebuild-status.md)")
|
|
else:
|
|
lines.append(f"- [上级目录](../{info.doc_dir.parent.name}.md)")
|
|
lines.append(f"- [API 总索引]({relative_link(index_path, API_ROOT / 'main.md')})")
|
|
|
|
return "\n".join(lines).rstrip() + "\n"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Scaffold parallel API directory index pages.")
|
|
parser.add_argument(
|
|
"--root-name",
|
|
default=DEFAULT_ROOT_NAME,
|
|
help="Canonical doc root name under docs/api, e.g. XCEngine or XCEditor.",
|
|
)
|
|
parser.add_argument(
|
|
"--include-root",
|
|
default=str(DEFAULT_INCLUDE_ROOT),
|
|
help="Source include root mirrored by the canonical doc tree.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
include_root = Path(args.include_root).resolve()
|
|
doc_root = API_ROOT / args.root_name
|
|
|
|
doc_root.mkdir(parents=True, exist_ok=True)
|
|
infos = iter_include_dirs(include_root, doc_root, args.root_name)
|
|
for info in infos:
|
|
info.doc_dir.mkdir(parents=True, exist_ok=True)
|
|
dir_index_path(info.doc_dir).write_text(build_dir_index(info), encoding="utf-8")
|
|
print(f"Generated {len(infos)} directory index files under {doc_root}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|