最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python代碼實現(xiàn)C++代碼依賴提取工具

 更新時間:2026年03月12日 08:40:37   作者:weixin_30777913  
這篇文章主要為大家詳細介紹了如何使用Python代碼實現(xiàn)一個C++代碼依賴提取工具,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

編寫一個Python程序代碼,輸入C/C++源文件名和函數(shù)名字符串,解析一個目錄下所有C/C++的源代碼,找到相關(guān)的宏名、變量名和函數(shù)名對應的具體實現(xiàn)代碼,以及和它們相關(guān)的具體實現(xiàn)代碼,按依賴性順序?qū)懭胍粋€standalone.cpp的文件里。

下面是一個使用 libclang 提取 C/C++ 函數(shù)及其依賴并生成 standalone.cpp 的 Python 程序框架。由于完整實現(xiàn)非常復雜,這里給出了核心思路和關(guān)鍵代碼,實際使用時需要根據(jù)項目情況調(diào)整。

import os
import sys
import argparse
from collections import defaultdict, deque
from clang.cindex import Index, CursorKind, TokenKind, Config

# 如果 libclang 不在默認路徑,需要設置 Config.set_library_path()
# Config.set_library_path('/usr/lib/llvm-10/lib')

class DependencyExtractor:
    def __init__(self, root_dir, target_func):
        self.root_dir = os.path.abspath(root_dir)
        self.target_func = target_func
        self.index = Index.create()
        self.symbols = {}          # 符號名 -> 符號信息(類型、定義位置、代碼片段等)
        self.dependencies = defaultdict(set)  # 符號名 -> 依賴的符號名集合
        self.referenced_by = defaultdict(set) # 反向依賴,用于拓撲排序
        self.file_content_cache = {} # 文件內(nèi)容緩存,用于提取代碼片段

    def parse_files(self):
        """遍歷目錄下所有 .c/.cpp/.h/.hpp 文件,解析并收集符號定義和引用關(guān)系"""
        for root, _, files in os.walk(self.root_dir):
            for file in files:
                if file.endswith(('.c', '.cpp', '.h', '.hpp')):
                    full_path = os.path.join(root, file)
                    self.parse_file(full_path)

    def parse_file(self, filepath):
        """解析單個文件,更新符號表和依賴關(guān)系"""
        tu = self.index.parse(filepath, args=['-x', 'c++', '-std=c++11'])  # 根據(jù)實際情況調(diào)整編譯參數(shù)
        if not tu:
            return

        # 遍歷所有頂層聲明,收集函數(shù)、變量、類型定義、宏
        self._visit_children(tu.cursor, filepath)

        # 額外遍歷預處理令牌獲取宏定義(因為宏定義在 AST 中可能不直接作為子節(jié)點出現(xiàn))
        self._collect_macros(tu, filepath)

    def _visit_children(self, cursor, filepath):
        """遞歸遍歷 AST,收集定義和引用關(guān)系"""
        # 處理當前節(jié)點
        kind = cursor.kind
        location = cursor.location
        if location.file and location.file.name != filepath:
            # 忽略來自頭文件的節(jié)點,因為該節(jié)點會在解析包含它的源文件時被捕獲
            # 但為了確保不遺漏,我們允許跨文件收集,但需要避免重復
            pass

        # 收集定義
        if cursor.is_definition():
            if kind == CursorKind.FUNCTION_DECL:
                self._add_symbol(cursor, 'function', filepath)
            elif kind == CursorKind.VAR_DECL and cursor.is_definition():
                # 檢查是否是全局變量(沒有父函數(shù))
                if self._is_global(cursor):
                    self._add_symbol(cursor, 'variable', filepath)
            elif kind in (CursorKind.STRUCT_DECL, CursorKind.ENUM_DECL,
                          CursorKind.TYPEDEF_DECL, CursorKind.CLASS_DECL):
                self._add_symbol(cursor, 'type', filepath)
            # 注意:宏定義通過 _collect_macros 收集

        # 收集函數(shù)體內(nèi)的引用(僅當當前節(jié)點是函數(shù)定義時)
        if kind == CursorKind.FUNCTION_DECL and cursor.is_definition():
            self._collect_references(cursor)

        # 繼續(xù)遍歷子節(jié)點
        for child in cursor.get_children():
            self._visit_children(child, filepath)

    def _is_global(self, cursor):
        """判斷變量是否為全局(沒有父函數(shù))"""
        parent = cursor.semantic_parent
        while parent:
            if parent.kind in (CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD):
                return False
            parent = parent.semantic_parent
        return True

    def _add_symbol(self, cursor, sym_type, filepath):
        """添加符號到符號表,記錄其代碼范圍"""
        name = cursor.spelling
        if not name:
            return
        # 如果已存在,保留最早的定義(簡單去重)
        if name in self.symbols:
            return
        start = cursor.extent.start
        end = cursor.extent.end
        # 提取代碼片段
        code = self._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):
        """從文件中提取指定行范圍的代碼"""
        if filepath not in self.file_content_cache:
            try:
                with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                    self.file_content_cache[filepath] = f.readlines()
            except Exception:
                return ""
        lines = self.file_content_cache[filepath]
        # 行號從1開始,轉(zhuǎn)換為索引
        start_idx = start_line - 1
        end_idx = end_line - 1
        return ''.join(lines[start_idx:end_idx+1])

    def _collect_references(self, func_cursor):
        """在函數(shù)體內(nèi)收集調(diào)用的其他函數(shù)、使用的全局變量、類型等"""
        func_name = func_cursor.spelling
        # 遍歷函數(shù)體內(nèi)的所有節(jié)點
        def visit_child(cursor):
            # 函數(shù)調(diào)用
            if cursor.kind == CursorKind.CALL_EXPR:
                ref = cursor.referenced
                if ref and ref.kind == CursorKind.FUNCTION_DECL:
                    callee = ref.spelling
                    if callee and callee != func_name:
                        self.dependencies[func_name].add(callee)
                        self.referenced_by[callee].add(func_name)
            # 變量引用
            elif cursor.kind == CursorKind.DECL_REF_EXPR:
                ref = cursor.referenced
                if ref and ref.kind == CursorKind.VAR_DECL and self._is_global(ref):
                    var_name = ref.spelling
                    if var_name:
                        self.dependencies[func_name].add(var_name)
                        self.referenced_by[var_name].add(func_name)
            # 類型引用(例如在變量聲明、參數(shù)中)
            # 這里簡化處理:通過獲取引用的類型定義來添加依賴
            elif cursor.kind in (CursorKind.VAR_DECL, CursorKind.PARM_DECL):
                type_cursor = cursor.type.get_declaration()
                if type_cursor and type_cursor.kind in (CursorKind.STRUCT_DECL,
                                                         CursorKind.ENUM_DECL,
                                                         CursorKind.TYPEDEF_DECL,
                                                         CursorKind.CLASS_DECL):
                    type_name = type_cursor.spelling
                    if type_name:
                        self.dependencies[func_name].add(type_name)
                        self.referenced_by[type_name].add(func_name)
            # 繼續(xù)遍歷子節(jié)點
            for child in cursor.get_children():
                visit_child(child)

        for child in func_cursor.get_children():
            visit_child(child)

    def _collect_macros(self, tu, filepath):
        """通過預處理令牌收集宏定義"""
        # 獲取所有預處理令牌
        tokens = tu.get_tokens(extent=tu.cursor.extent)
        current_macro = None
        macro_lines = []
        macro_start = None
        for token in tokens:
            if token.kind == TokenKind.PUNCTUATION and token.spelling == '#':
                # 遇到 #,可能是宏定義開始
                # 簡單處理:下一令牌如果是 'define',則開始收集宏
                pass  # 這里需要復雜的狀態(tài)機,省略實現(xiàn)細節(jié)
        # 實際上 libclang 提供了 CursorKind.MACRO_DEFINITION,但需要遍歷預處理實體?
        # 簡單方法:遍歷所有宏定義光標
        for cursor in tu.cursor.get_children():
            if cursor.kind == CursorKind.MACRO_DEFINITION:
                name = cursor.spelling
                # 獲取宏定義的范圍
                extent = cursor.extent
                start = extent.start
                end = extent.end
                code = self._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
                }

    def resolve_dependencies(self):
        """從目標函數(shù)出發(fā),收集所有依賴的符號(廣度優(yōu)先)"""
        if self.target_func not in self.symbols:
            print(f"錯誤:未找到函數(shù) {self.target_func} 的定義")
            sys.exit(1)

        visited = set()
        queue = deque([self.target_func])
        while queue:
            sym = queue.popleft()
            if sym in visited:
                continue
            visited.add(sym)
            # 獲取該符號的依賴
            deps = self.dependencies.get(sym, set())
            for dep in deps:
                if dep not in visited:
                    queue.append(dep)
        return visited

    def topological_sort(self, symbols):
        """對符號集合進行拓撲排序(被依賴的先輸出)"""
        # 構(gòu)建子圖
        graph = {s: set() for s in symbols}
        for s in symbols:
            for dep in self.dependencies.get(s, set()):
                if dep in symbols:
                    graph[s].add(dep)

        # 計算入度
        in_degree = {s: 0 for s in symbols}
        for s in symbols:
            for dep in graph[s]:
                in_degree[dep] += 1

        # Kahn 算法
        zero_in = deque([s for s in symbols if in_degree[s] == 0])
        topo = []
        while zero_in:
            node = zero_in.popleft()
            topo.append(node)
            for dep in graph[node]:
                in_degree[dep] -= 1
                if in_degree[dep] == 0:
                    zero_in.append(dep)

        if len(topo) != len(symbols):
            print("警告:存在循環(huán)依賴,將按任意順序輸出")
            # 簡單返回原集合
            return list(symbols)
        return topo

    def write_standalone(self, output_file='standalone.cpp'):
        """生成 standalone.cpp 文件"""
        # 獲取依賴的所有符號
        symbols_to_include = self.resolve_dependencies()
        # 按類型分組,以便將類型定義放在前面
        type_symbols = [s for s in symbols_to_include if self.symbols[s]['type'] == 'type']
        var_symbols = [s for s in symbols_to_include if self.symbols[s]['type'] == 'variable']
        func_symbols = [s for s in symbols_to_include if self.symbols[s]['type'] == 'function']
        macro_symbols = [s for s in symbols_to_include if self.symbols[s]['type'] == 'macro']

        # 拓撲排序:類型和變量之間可能有依賴,但簡單起見,我們先輸出類型,再輸出變量,再輸出函數(shù)
        # 更精確的做法是對所有符號一起拓撲排序
        all_symbols = list(symbols_to_include)
        topo_order = self.topological_sort(all_symbols)

        with open(output_file, 'w', encoding='utf-8') as f:
            f.write("http:// Standalone generated file\n")
            f.write("http:// 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")

            # 按照拓撲順序輸出代碼片段
            for sym in topo_order:
                info = self.symbols[sym]
                f.write(f"http:// {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)} 個符號。")

def main():
    parser = argparse.ArgumentParser(description='提取 C/C++ 函數(shù)及其依賴,生成 standalone.cpp')
    parser.add_argument('directory', help='源代碼根目錄')
    parser.add_argument('function', help='目標函數(shù)名')
    args = parser.parse_args()

    extractor = DependencyExtractor(args.directory, args.function)
    extractor.parse_files()
    extractor.write_standalone()

if __name__ == '__main__':
    main()

使用說明

1.安裝依賴

  • 安裝 libclang(例如通過 apt install libclang-10-dev)和 Python 綁定 clangpip install clang)。
  • 如果 libclang 不在默認路徑,需要在代碼開頭設置 Config.set_library_path()

2.運行

python extract.py /path/to/project my_function

3.輸出:生成 standalone.cpp,包含目標函數(shù)及其依賴的所有函數(shù)、全局變量、類型定義和宏定義,按拓撲順序排列(被依賴的在前)。

注意事項

  • 此實現(xiàn)為概念驗證,實際使用需處理更多邊界情況(如函數(shù)重載、命名空間、類成員函數(shù)、模板等)。
  • 宏定義的收集依賴預處理令牌,當前代碼未完整實現(xiàn),可能需要改用其他方法(如遍歷預處理實體)。
  • 依賴分析僅包含直接的函數(shù)調(diào)用、全局變量引用和類型引用,未考慮通過指針、回調(diào)等間接調(diào)用。
  • 代碼提取基于行范圍,可能不準確(例如宏定義跨多行,或函數(shù)體包含大括號嵌套)。
  • 對于標準庫函數(shù)(如 printf),不會提取其實現(xiàn),但保留了頭文件包含語句。

如果需要更完善的解決方案,可以考慮使用成熟的工具如 gcc -E 預處理后分析,或基于 clangASTMatcher

到此這篇關(guān)于Python代碼實現(xiàn)C++代碼依賴提取工具的文章就介紹到這了,更多相關(guān)Python提取C++代碼依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

法库县| 安庆市| 清远市| 独山县| 卢湾区| 衡阳市| 台东县| 泊头市| 衡东县| 宁化县| 万全县| 长白| 龙江县| 乐陵市| 高密市| 广水市| 衡阳市| 广宗县| 密云县| 道孚县| 盐山县| 安岳县| 上高县| 威远县| 扎赉特旗| 平果县| 阳曲县| 惠州市| 柏乡县| 无为县| 双江| 宜都市| 榆林市| 呼伦贝尔市| 永春县| 应城市| 信阳市| 禄丰县| 噶尔县| 乌鲁木齐县| 库车县|