在软件开发过程中,类之间的循环依赖会使代码变得脆弱,难以修改,并且在二进制级别上可能导致构建问题。手动检查源代码以发现和修复这些问题可能会非常耗时。本文将介绍如何使用开源工具DeepEnds和Doxygen来分析项目代码。
DeepEnds是一个开源工具,它可以通过特殊的注释生成各种图表(如类图、调用者和被调用者图等)。它原生支持多种编程语言(如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格式。
为了设置问题,首先需要生成文档。文档是通过运行Doxygen生成XML输出,然后输入到DeepEnds中生成包含注释的源文件,这些注释将被Doxygen处理。以下是一个批处理文件的例子,它运行Doxygen并生成XML输出,然后输入到DeepEnds中。
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
在这个例子中,使用了C++代码。Doxygen运行使用不同的文件,第一个设置输出目录、是否生成引用关系、是否生成XML以及XML输出的路径。DeepEnds运行创建了源文件Dot\arch.cpp,它从doxygen\xml目录中的XML文件中读取数据。文件dummy.xml实际上并不存在,它使用了解析Doxygen XML的默认参数值,这些值对于C++来说是合适的,但其他语言可能需要调整。
第二次Doxygen运行通过重新解析源代码并包含写入到Dot\arch.cpp的DeepEnds输出来创建HTML输出。
Doxygen HTML报告的第一页显示了命名空间和类之间的依赖关系图(尽管这里没有显示类)。然后是一个表格,显示了从图表及其子图表计算出的主要统计数据。第一列包含命名空间或类的名称,第二列显示是否存在循环。接下来的九列基于边数(E)、部分数(P)和节点数(N)的公式,这些公式在“为什么偏爱循环复杂度?”一文中有详细讨论。接下来的两列是对应于形成边的依赖关系的外部依赖项的数量及其在遍历树时的最大值。然后是遍历树时源代码行数的总和,其次是拟合对数正态分布的结果,详细内容请参考“代码行数统计”,最后是树中的最大值。
如果没有叶节点(在这种情况下是类),那么下一个表格将列出这些类与它们包含的代码行数的对比,这些代码行数由Doxygen计算。不幸的是,对于C++,这似乎只是类声明的大小。下一个表格列出了FEA::FileIO命名空间使用的16个类(如前一个表格中的外部依赖计数所提到的)。
接下来是一个表格,列出了FEA::FileIO命名空间内部使用的类,因此形成了图表中定向边的目的地。
最后,图表被报告为一个结构矩阵。
虽然本文分析的源代码是C++,但这种技术并不局限于对象代码。然而,构建层次结构存在问题,在示例中是从命名空间形成的。另一种层次结构可能来自文件夹结构,尽管DeepEnds目前不支持。
可能Doxygen支持的长语言列表不包括感兴趣的语言,或者情况甚至不是基于语言的。对于这样的问题,用户可以生成使用Doxygen模式的XML(或者更简单地,创建感兴趣的XML元素),然后让DeepEnds生成报告。编写定制的解析器也有优势,可以克服Doxygen解析器的任何限制,比如上面提到的C++代码行数的计数。
正如在引言中提到的,Doxygen原生支持C#,并且有Visual Basic的扩展。DeepEnds本身有基于Roslyn的C#和Visual Basic解析器,并且会使用Mono.Cecil反编译.NET程序集,因此建议使用这些而不是Doxygen来解析源代码。