C++代码依赖提取工具
编写一个Python程序代码输入C/C源文件名和函数名字符串解析一个目录下所有C/C的源代码找到相关的宏名、变量名和函数名对应的具体实现代码以及和它们相关的具体实现代码按依赖性顺序写入一个standalone.cpp的文件里。下面是一个使用libclang提取 C/C 函数及其依赖并生成standalone.cpp的 Python 程序框架。由于完整实现非常复杂这里给出了核心思路和关键代码实际使用时需要根据项目情况调整。importosimportsysimportargparsefromcollectionsimportdefaultdict,dequefromclang.cindeximportIndex,CursorKind,TokenKind,Config# 如果 libclang 不在默认路径需要设置 Config.set_library_path()# Config.set_library_path(/usr/lib/llvm-10/lib)classDependencyExtractor:def__init__(self,root_dir,target_func):self.root_diros.path.abspath(root_dir)self.target_functarget_func self.indexIndex.create()self.symbols{}# 符号名 - 符号信息类型、定义位置、代码片段等self.dependenciesdefaultdict(set)# 符号名 - 依赖的符号名集合self.referenced_bydefaultdict(set)# 反向依赖用于拓扑排序self.file_content_cache{}# 文件内容缓存用于提取代码片段defparse_files(self):遍历目录下所有 .c/.cpp/.h/.hpp 文件解析并收集符号定义和引用关系forroot,_,filesinos.walk(self.root_dir):forfileinfiles:iffile.endswith((.c,.cpp,.h,.hpp)):full_pathos.path.join(root,file)self.parse_file(full_path)defparse_file(self,filepath):解析单个文件更新符号表和依赖关系tuself.index.parse(filepath,args[-x,c,-stdc11])# 根据实际情况调整编译参数ifnottu:return# 遍历所有顶层声明收集函数、变量、类型定义、宏self._visit_children(tu.cursor,filepath)# 额外遍历预处理令牌获取宏定义因为宏定义在 AST 中可能不直接作为子节点出现self._collect_macros(tu,filepath)def_visit_children(self,cursor,filepath):递归遍历 AST收集定义和引用关系# 处理当前节点kindcursor.kind locationcursor.locationiflocation.fileandlocation.file.name!filepath:# 忽略来自头文件的节点因为该节点会在解析包含它的源文件时被捕获# 但为了确保不遗漏我们允许跨文件收集但需要避免重复pass# 收集定义ifcursor.is_definition():ifkindCursorKind.FUNCTION_DECL:self._add_symbol(cursor,function,filepath)elifkindCursorKind.VAR_DECLandcursor.is_definition():# 检查是否是全局变量没有父函数ifself._is_global(cursor):self._add_symbol(cursor,variable,filepath)elifkindin(CursorKind.STRUCT_DECL,CursorKind.ENUM_DECL,CursorKind.TYPEDEF_DECL,CursorKind.CLASS_DECL):self._add_symbol(cursor,type,filepath)# 注意宏定义通过 _collect_macros 收集# 收集函数体内的引用仅当当前节点是函数定义时ifkindCursorKind.FUNCTION_DECLandcursor.is_definition():self._collect_references(cursor)# 继续遍历子节点forchildincursor.get_children():self._visit_children(child,filepath)def_is_global(self,cursor):判断变量是否为全局没有父函数parentcursor.semantic_parentwhileparent:ifparent.kindin(CursorKind.FUNCTION_DECL,CursorKind.CXX_METHOD):returnFalseparentparent.semantic_parentreturnTruedef_add_symbol(self,cursor,sym_type,filepath):添加符号到符号表记录其代码范围namecursor.spellingifnotname:return# 如果已存在保留最早的定义简单去重ifnameinself.symbols:returnstartcursor.extent.start endcursor.extent.end# 提取代码片段codeself._extract_code(filepath,start.line,end.line)self.symbols[name]{type:sym_type,file:filepath,start_line:start.line,end_line:end.line,code:code,cursor:cursor}def_extract_code(self,filepath,start_line,end_line):从文件中提取指定行范围的代码iffilepathnotinself.file_content_cache:try:withopen(filepath,r,encodingutf-8,errorsignore)asf:self.file_content_cache[filepath]f.readlines()exceptException:returnlinesself.file_content_cache[filepath]# 行号从1开始转换为索引start_idxstart_line-1end_idxend_line-1return.join(lines[start_idx:end_idx1])def_collect_references(self,func_cursor):在函数体内收集调用的其他函数、使用的全局变量、类型等func_namefunc_cursor.spelling# 遍历函数体内的所有节点defvisit_child(cursor):# 函数调用ifcursor.kindCursorKind.CALL_EXPR:refcursor.referencedifrefandref.kindCursorKind.FUNCTION_DECL:calleeref.spellingifcalleeandcallee!func_name:self.dependencies[func_name].add(callee)self.referenced_by[callee].add(func_name)# 变量引用elifcursor.kindCursorKind.DECL_REF_EXPR:refcursor.referencedifrefandref.kindCursorKind.VAR_DECLandself._is_global(ref):var_nameref.spellingifvar_name:self.dependencies[func_name].add(var_name)self.referenced_by[var_name].add(func_name)# 类型引用例如在变量声明、参数中# 这里简化处理通过获取引用的类型定义来添加依赖elifcursor.kindin(CursorKind.VAR_DECL,CursorKind.PARM_DECL):type_cursorcursor.type.get_declaration()iftype_cursorandtype_cursor.kindin(CursorKind.STRUCT_DECL,CursorKind.ENUM_DECL,CursorKind.TYPEDEF_DECL,CursorKind.CLASS_DECL):type_nametype_cursor.spellingiftype_name:self.dependencies[func_name].add(type_name)self.referenced_by[type_name].add(func_name)# 继续遍历子节点forchildincursor.get_children():visit_child(child)forchildinfunc_cursor.get_children():visit_child(child)def_collect_macros(self,tu,filepath):通过预处理令牌收集宏定义# 获取所有预处理令牌tokenstu.get_tokens(extenttu.cursor.extent)current_macroNonemacro_lines[]macro_startNonefortokenintokens:iftoken.kindTokenKind.PUNCTUATIONandtoken.spelling#:# 遇到 #可能是宏定义开始# 简单处理下一令牌如果是 define则开始收集宏pass# 这里需要复杂的状态机省略实现细节# 实际上 libclang 提供了 CursorKind.MACRO_DEFINITION但需要遍历预处理实体# 简单方法遍历所有宏定义光标forcursorintu.cursor.get_children():ifcursor.kindCursorKind.MACRO_DEFINITION:namecursor.spelling# 获取宏定义的范围extentcursor.extent startextent.start endextent.end codeself._extract_code(filepath,start.line,end.line)self.symbols[name]{type:macro,file:filepath,start_line:start.line,end_line:end.line,code:code,cursor:cursor}defresolve_dependencies(self):从目标函数出发收集所有依赖的符号广度优先ifself.target_funcnotinself.symbols:print(f错误未找到函数{self.target_func}的定义)sys.exit(1)visitedset()queuedeque([self.target_func])whilequeue:symqueue.popleft()ifsyminvisited:continuevisited.add(sym)# 获取该符号的依赖depsself.dependencies.get(sym,set())fordepindeps:ifdepnotinvisited:queue.append(dep)returnvisiteddeftopological_sort(self,symbols):对符号集合进行拓扑排序被依赖的先输出# 构建子图graph{s:set()forsinsymbols}forsinsymbols:fordepinself.dependencies.get(s,set()):ifdepinsymbols:graph[s].add(dep)# 计算入度in_degree{s:0forsinsymbols}forsinsymbols:fordepingraph[s]:in_degree[dep]1# Kahn 算法zero_indeque([sforsinsymbolsifin_degree[s]0])topo[]whilezero_in:nodezero_in.popleft()topo.append(node)fordepingraph[node]:in_degree[dep]-1ifin_degree[dep]0:zero_in.append(dep)iflen(topo)!len(symbols):print(警告存在循环依赖将按任意顺序输出)# 简单返回原集合returnlist(symbols)returntopodefwrite_standalone(self,output_filestandalone.cpp):生成 standalone.cpp 文件# 获取依赖的所有符号symbols_to_includeself.resolve_dependencies()# 按类型分组以便将类型定义放在前面type_symbols[sforsinsymbols_to_includeifself.symbols[s][type]type]var_symbols[sforsinsymbols_to_includeifself.symbols[s][type]variable]func_symbols[sforsinsymbols_to_includeifself.symbols[s][type]function]macro_symbols[sforsinsymbols_to_includeifself.symbols[s][type]macro]# 拓扑排序类型和变量之间可能有依赖但简单起见我们先输出类型再输出变量再输出函数# 更精确的做法是对所有符号一起拓扑排序all_symbolslist(symbols_to_include)topo_orderself.topological_sort(all_symbols)withopen(output_file,w,encodingutf-8)asf:f.write(// Standalone generated file\n)f.write(// Include necessary standard headers (you may need to add more)\n)f.write(#include stdio.h\n)f.write(#include stdlib.h\n)f.write(#include string.h\n\n)# 按照拓扑顺序输出代码片段forsymintopo_order:infoself.symbols[sym]f.write(f//{info[type]}:{sym}(from{info[file]}:{info[start_line]})\n)f.write(info[code])f.write(\n\n)print(f成功生成{output_file}包含{len(topo_order)}个符号。)defmain():parserargparse.ArgumentParser(description提取 C/C 函数及其依赖生成 standalone.cpp)parser.add_argument(directory,help源代码根目录)parser.add_argument(function,help目标函数名)argsparser.parse_args()extractorDependencyExtractor(args.directory,args.function)extractor.parse_files()extractor.write_standalone()if__name____main__:main()使用说明安装依赖安装libclang例如通过apt install libclang-10-dev和 Python 绑定clangpip install clang。如果libclang不在默认路径需要在代码开头设置Config.set_library_path()。运行python extract.py /path/to/project my_function输出生成standalone.cpp包含目标函数及其依赖的所有函数、全局变量、类型定义和宏定义按拓扑顺序排列被依赖的在前。注意事项此实现为概念验证实际使用需处理更多边界情况如函数重载、命名空间、类成员函数、模板等。宏定义的收集依赖预处理令牌当前代码未完整实现可能需要改用其他方法如遍历预处理实体。依赖分析仅包含直接的函数调用、全局变量引用和类型引用未考虑通过指针、回调等间接调用。代码提取基于行范围可能不准确例如宏定义跨多行或函数体包含大括号嵌套。对于标准库函数如printf不会提取其实现但保留了头文件包含语句。如果需要更完善的解决方案可以考虑使用成熟的工具如gcc -E预处理后分析或基于clang的ASTMatcher。

相关新闻

Point2Mesh源码解析:网络结构与损失函数设计深度剖析

Point2Mesh源码解析:网络结构与损失函数设计深度剖析

Point2Mesh源码解析:网络结构与损失函数设计深度剖析 【免费下载链接】point2mesh Reconstruct Watertight Meshes from Point Clouds [SIGGRAPH 2020] 项目地址: https://gitcode.com/gh_mirrors/po/point2mesh Point2Mesh是一个基于深度学习的点云重建项目…

2026/7/5 10:02:22 阅读更多 →
SecureLogin源代码解析:密码派生与数据加密的实现原理

SecureLogin源代码解析:密码派生与数据加密的实现原理

SecureLogin源代码解析:密码派生与数据加密的实现原理 【免费下载链接】securelogin This version wont be maintained! 项目地址: https://gitcode.com/gh_mirrors/se/securelogin SecureLogin是一款专注于密码安全管理的开源项目,其核心功能围绕…

2026/7/6 13:56:34 阅读更多 →
提升游戏性能的10个ASTC编码技巧:从LDR到HDR全攻略

提升游戏性能的10个ASTC编码技巧:从LDR到HDR全攻略

提升游戏性能的10个ASTC编码技巧:从LDR到HDR全攻略 【免费下载链接】astc-encoder The Arm ASTC Encoder, a compressor for the Adaptive Scalable Texture Compression data format. 项目地址: https://gitcode.com/gh_mirrors/as/astc-encoder ASTC&#…

2026/7/6 13:03:59 阅读更多 →

最新新闻

UE4SS:解锁虚幻引擎游戏无限可能的终极修改工具指南

UE4SS:解锁虚幻引擎游戏无限可能的终极修改工具指南

UE4SS:解锁虚幻引擎游戏无限可能的终极修改工具指南 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/re/RE-UE4SS …

2026/7/6 19:08:46 阅读更多 →
如何安装和配置EnergyBar:10分钟快速上手教程

如何安装和配置EnergyBar:10分钟快速上手教程

如何安装和配置EnergyBar:10分钟快速上手教程 【免费下载链接】EnergyBar Supercharge your Macs Touch Bar. 项目地址: https://gitcode.com/gh_mirrors/en/EnergyBar 想要快速上手EnergyBar,让你的Mac Touch Bar变得更强大吗?Energy…

2026/7/6 19:08:46 阅读更多 →
如何快速入门Encog:5个简单步骤构建你的第一个神经网络

如何快速入门Encog:5个简单步骤构建你的第一个神经网络

如何快速入门Encog:5个简单步骤构建你的第一个神经网络 【免费下载链接】encog-java-core 项目地址: https://gitcode.com/gh_mirrors/en/encog-java-core Encog是一个强大的Java机器学习框架,专为构建和训练神经网络而设计。无论你是机器学习新…

2026/7/6 19:08:46 阅读更多 →
Linkora开发指南:如何为这款KMP应用贡献代码和新功能

Linkora开发指南:如何为这款KMP应用贡献代码和新功能

Linkora开发指南:如何为这款KMP应用贡献代码和新功能 【免费下载链接】Linkora Local-first multiplatform link organizer with optional self-hosted sync. 项目地址: https://gitcode.com/gh_mirrors/li/Linkora Linkora是一款基于Kotlin Multiplatform&a…

2026/7/6 19:06:45 阅读更多 →
从0到1掌握docopt.rs:Rust开发者必备的命令行解析库实战指南

从0到1掌握docopt.rs:Rust开发者必备的命令行解析库实战指南

从0到1掌握docopt.rs:Rust开发者必备的命令行解析库实战指南 【免费下载链接】docopt.rs Docopt for Rust (command line argument parser). 项目地址: https://gitcode.com/gh_mirrors/do/docopt.rs docopt.rs是Rust语言中一款强大的命令行参数解析库&#…

2026/7/6 19:06:45 阅读更多 →
Vue开发者必看:ArcGIS Maps SDK for JavaScript Vite模板极速上手教程

Vue开发者必看:ArcGIS Maps SDK for JavaScript Vite模板极速上手教程

Vue开发者必看:ArcGIS Maps SDK for JavaScript Vite模板极速上手教程 【免费下载链接】jsapi-resources A collection of resources for developers using the ArcGIS Maps SDK for JavaScript. 项目地址: https://gitcode.com/gh_mirrors/js/jsapi-resources …

2026/7/6 19:04:44 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻