使用 Doxygen 进行依赖分析






4.14/5 (3投票s)
使用 Doxygen 解析源代码并生成报告来进行依赖分析
引言
Doxygen 是一种从源代码(API 规范、类图、调用者和被调用者图等)生成文档的系统,它利用特殊的注释。它原生支持多种语言作为输入(C/C++、C#、D、Fortran、IDL、Java、Objective-C、PHP、Python、TCL、VHDL),其他语言则通过扩展支持(Perl、JavaScript、Object Pascal、Visual Basic、MatLab、Pro*C、Assembly、Lua、GLSL Shader、Qt QML、GOB-doc、Prolog、CAPL)。生成的输出有多种格式,本文特别关注 HTML 和 XML。
在开发软件时,类之间存在循环依赖等问题会使代码易受修改影响,如果它们发生在二进制文件层面,还会导致构建问题。通过遍历源代码来查找和修复这些问题可能非常耗时,因此本文将使用开源工具 DeepEnds(Visual Studio 扩展、NuGet 包)分析一些项目代码。DeepEnds 可用的选项包括读取 Doxygen XML 和写入带有 Doxygen 注释的源文件,本文将演示这两种用法。
本文其余部分使用的具体示例是 C++ 代码。
问题设置
文档通过运行 Doxygen 的批处理文件生成,Doxygen 生成 XML 输出,然后将其输入到 DeepEnds 中,生成一个包含 Doxygen 要处理的注释的源文件。
rmdir /s /q doxygen\xml
doxygen.exe Doxyxml
DeepEnds.Console.exe doxygen=Dot\arch.cpp doxygen\xml\dummy.xml
rmdir /s /q doxygen\html
doxygen.exe Doxyfile
Doxygen 运行使用不同的文件,第一个文件设置
OUTPUT_DIRECTORY       = doxygen
REFERENCES_RELATION    = YES
GENERATE_XML           = YES
XML_OUTPUT             = xml
DeepEnds 运行从目录 doxygen\xml 中的 XML 文件创建源文件 Dot\arch.cpp,文件 dummy.xml 实际上不存在。它使用与解析 Doxygen XML 相关的参数的默认值,这些值对于 C++ 来说很好,但可能需要针对其他语言进行修改。
第二次 Doxygen 运行然后通过重新解析源代码并包含 DeepEnds 写入 Dot\arch.cpp 的输出来创建 HTML 输出。
Doxygen HTML 报告页面
首先,显示了使用 Dot 生成的命名空间和类之间依赖关系的图(尽管此处没有显示任何类)。

然后是一个表格,其中包含根据图及其子图计算出的主要统计数据。第一列包含命名空间或类的名称,第二列指示是否存在循环。接下来的九列的公式基于边数 (E)、部分数 (P) 和节点数 (N),这些内容在为何偏爱圈复杂度?中进行了讨论。具体来说,是树中该级别的值以及树中三个公式 (E+P-N)/N、E+P-N 和 N 的总和和最大值。接下来的两列是与形成边的依赖关系对应的外部数量及其遍历树时达到的最大值。然后是遍历树时源代码行数的总和,接着是代码行数统计中详述的对数正态分布拟合结果,再接着是树中的最大值。
| 节 | 环 | (E + P - N) / N | E + P - N | N | 外部 | SLOC | SLOC 概率 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 值 | 最大值 | Sum | 值 | 最大值 | Sum | 值 | 最大值 | Sum | Count | 最大值 | 最大值 | Sum | Lower | Exp | Upper | 最大值 | ||
| FEA.FileIO | 0.00 | 0.56 | 0.56 | 0 | 5 | 5 | 4 | 9 | 24 | 16 | 12 | 60 | 540 | 10 | 24 | 56 | 28 | |
| FEA.FileIO.Abaqus | 0.00 | 0.00 | 0.00 | 0 | 0 | 0 | 2 | 2 | 2 | 10 | 6 | 28 | 44 | 21 | 21 | |||
| FEA.FileIO.Common | 0.00 | 0.00 | 0.00 | 0 | 0 | 0 | 7 | 7 | 7 | 12 | 6 | 40 | 193 | 27 | 27 | |||
| FEA.FileIO.Ideas | 0.56 | 0.56 | 0.56 | 5 | 5 | 5 | 9 | 9 | 9 | 12 | 12 | 54 | 230 | 22 | 22 | |||
| FEA.FileIO.Vtk | 0.00 | 0.00 | 0.00 | 0 | 0 | 0 | 2 | 2 | 2 | 11 | 9 | 60 | 73 | 28 | 28 | |||
如果该级别有任何叶节点(在本例中是类),那么下一个表格将列出这些类及其Doxygen统计的代码行数。不幸的是,对于 C++,这似乎只显示了类声明的大小。
下一个表格列出了 FEA::FileIO 命名空间之外的 16 个被其使用的类(如上表中外部计数所述)。
| 外部依赖 | 
|---|
| FEA.ElementFactory | 
| FEA.Elements.ElementDefinition | 
| FEA.ElementSet.ElementVisitor | 
| FEA.ElementSet.Mesh | 
| FEA.ElementSet.SetOfElements | 
| FEA.Field.Base | 
| FEA.Field.Elemental | 
| FEA.Field.ElementalFieldVisitor | 
| FEA.Field.FieldVisitor | 
| FEA.Field.Nodal | 
| FEA.Field.NodesElements | 
| FEA.Field.Types | 
| FEA.Field.Varying | 
| FEA.Set.System | 
| FEA.Surface | 
| FEA.Topology.ElementHandler | 
接着是一个表格,列出了 FEA::FileIO 命名空间中被其使用的类,从而构成了图中定向边的目标。
| 内部依赖 | 
|---|
| FEA.FileIO.Common.FileReader | 
| FEA.FileIO.Common.FileWriter | 
然后是一系列表格,扩展了上一个表格,以显示构成图边缘的基础类依赖关系。
| FEA.FileIO.Abaqus | → | FEA.FileIO.Common | 
|---|---|---|
| FEA.FileIO.Abaqus.ReadAbaqusInp | → | FEA.FileIO.Common.FileReader | 
| FEA.FileIO.Abaqus.WriteAbaqusInp | → | FEA.FileIO.Common.FileWriter | 
| FEA.FileIO.Ideas | → | FEA.FileIO.Common | 
| FEA.FileIO.Ideas.ReadIdeas | → | FEA.FileIO.Common.FileReader | 
| FEA.FileIO.Ideas.WriteIdeas | → | FEA.FileIO.Common.FileWriter | 
| FEA.FileIO.Vtk | → | FEA.FileIO.Common | 
| FEA.FileIO.Vtk.ReadVtk | → | FEA.FileIO.Common.FileReader | 
| FEA.FileIO.Vtk.WriteVtk | → | FEA.FileIO.Common.FileWriter | 
最后(多余地),该图以结构矩阵的形式报告。
| Common | \ | |||
|---|---|---|---|---|
| Abaqus | 1 | \ | ||
| 想法 | 1 | \ | ||
| Vtk | 1 | \ | 
讨论
尽管本文分析的源代码是 C++,但该技术不限于目标代码,然而存在构建层次结构的问题,在示例中该层次结构是由命名空间形成的。替代的层次结构可以由文件夹结构形成,尽管 DeepEnds 目前不支持此功能。
Doxygen 支持的语言列表很长,但可能不包含您感兴趣的语言——也许甚至不是基于语言的。对于这样的问题,用户可以使用 Doxygen 模式生成 XML(或者更简单地,创建感兴趣的 XML 元素),然后让 DeepEnds 生成报告。编写自定义解析器还具有克服 Doxygen 内部解析器的任何限制的优点,例如上面提到的 C++ 代码行数统计。
引言中提到 Doxygen 原生支持 C#,并且有 Visual Basic 的扩展。DeepEnds 本身拥有基于 Roslyn 的 C# 和 Visual Basic 解析器,并将使用 Mono.Cecil 反编译 .NET 程序集,因此建议使用它们而不是 Doxygen 来解析源代码。
考虑到 DeepEnds 的输出可能足够复杂,导致 Doxygen 运行挂起,因此最好通过向 doxygen=Dot\arch.cpp 提供替代参数(例如 report=report.html)来生成独立的 HTML 报告。此 HTML 报告没有图的图片,可以通过使用另一个参数(例如 graph=graph.dgml)生成一些替代输出,从而在 Visual Studio 中查看图。
历史
- 2016/11/12: 首次发布

