Page 133 - 《软件学报》2026年第7期
P. 133

2818                                                       软件学报  2026  年第  37  卷第  7  期



                                   (a) TPL成分模块划分                   (b) TPL依赖关系检测
                                                                                               OUTPUT
                                                              消除内部依赖          建立外部依赖
                                                             检测模块内部文件
                     INPUT                     TPL A module  引用关系, 隐藏仅存       检查不同模块
                                                             在内部引用关系的         之间的依赖关系
                                   模块划分                          文件
                                                                                                TPL A
                                 根据目录结构及          ...                     ...
                                  元数据划分
                 目标代码TPL清单                                    消除内部依赖          建立外部依赖         TPL B  TPL C
                                                             检测模块内部文件
                                              TPL B module  引用关系, 隐藏仅存        检查不同模块
                                                             在内部引用关系的         之间的依赖关系
                                                                 文件                           TPL D

                                                 图 7 TPL  依赖关系检测框架

                    ● 标准  1: 基于目录命名的     TPL  组成部分判定. 若复用文件的路径中包含             TPL  名称, 则可以直接判定该文件
                 为该  TPL  的一部分. 例如, spirv-tools 复用文件路径“skia/third_party/externals/spirv-tools/source/opt/optimizer.cpp”中
                 包含“spirv-tools”, 因此该文件应归属于    spirv-tools 库.
                    ● 标准  2: 基于  TPL  头文件位置的   TPL  组成部分判定. 若复用文件所在目录包含            TPL  的头文件 (.h  文件), 则
                 该目录下的文件可归属于该          TPL. 例如, 若目录中包含     libfoo.h  文件, 且该目录下文件与   libfoo  发生代码复用, 则该
                 目录下的复用文件应归属于          libfoo  库.
                    ● 标准  3: 基于  TPL  元数据文件位置的     TPL  组成部分判定. 若复用文件所在目录包含            TPL  的元数据文件    (如
                 README、LICENSE、COPYING), 则该文件应归属于相应           TPL. 为提高判定的准确性, 我们将检查元数据文件的
                 内容, 验证其中是否包含       TPL  名称. 如果验证成功, 确认元数据文件属于该            TPL, 并将此目录下的复用文件归为该
                 TPL  的组成部分.
                    ● 标准  4: 基于函数复用数量的       TPL  组成部分判定. 在缺乏明确线索的情况下, 我们依赖函数复用频率来判
                 定复用文件是否属于        TPL  的实际组成部分. 具体方法是统计每个复用文件所在目录的复用频率 (复用频率=该目
                 录中与   TPL  相关的复用函数总数/该目录中函数总数), 然后选择复用频率最高的目录, 将此目录下的复用文件归
                 为该  TPL  的组成部分.
                    对于每个    TPL, 若其某些复用文件已通过前         3  个标准之一归类, 则剩余未分类的文件不再视为该               TPL  的组成
                 部分. 由于这些文件与      TPL  的关联度较低, 因此不予归属. 仅当所有复用文件无法通过前                  3  个标准判定时, 才使用
                 第  4  个标准来确定哪些文件属于该        TPL  的组成部分. 此外, 已归类的文件不会被重新划分.
                  3.3.2    TPL  依赖关系检测
                    在  CAnalyzer 中, 目标软件的每个源文件首先通过          TPL  检测阶段被映射至其所属的         TPL. 由此, 每个  TPL  可
                 抽象为一个包含多个源文件的“文件集”. 在依赖关系检测阶段, CAnalyzer 对文件级依赖 (包括头文件引用、符号
                 引用与链接依赖) 进行静态分析, 并将这些依赖关系在文件集层面进行聚合, 从而构建出                          TPL  间的依赖关系图. 为
                 建立这一映射关系, 我们首先形式化定义软件项目中任意文件对之间的依赖判定规则.
                    (1) 文件对依赖关系分析. 在同一软件项目中, 文件             A  的执行依赖于文件     B  的存在或正确性时, 文件      A  对文件
                 B  就存在依赖关系. 此类依赖关系可以细分为以下几种.
                    (a) 直接依赖关系. 文件     A  通过显式引用文件     B  的资源而形成依赖, 通常采用以下两种方式.
                    ● #include 指令声明. 文件  A  通过“#include”指令包含文件  B (如.h 或.hpp), 从而能够访问文件    B  中声明的变量、
                 函数或类型定义. 如图      8  所示, 在  C++项目中, Main.cpp  包含了  Utility.h, 形成直接依赖关系. 我们通过解析源码中
                 的“#include”语句, 并匹配头文件路径, 来确认文件         Main.cpp  是否依赖文件   Utility.h. 对于存在多个同名头文件的
                 情况, 遵循  GNU GCC  [33] 的路径解析规则, 选择路径最近或明确指定的头文件. 如果路径冲突无法解决, 则将此类
                 冲突留待后续间接依赖分析.
   128   129   130   131   132   133   134   135   136   137   138