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

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

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

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

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

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

# 如果 libclang 不在默認(rèn)路徑,需要設(shè)置 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 = {}          # 符號(hào)名 -> 符號(hào)信息(類型、定義位置、代碼片段等)
        self.dependencies = defaultdict(set)  # 符號(hào)名 -> 依賴的符號(hào)名集合
        self.referenced_by = defaultdict(set) # 反向依賴,用于拓?fù)渑判?
        self.file_content_cache = {} # 文件內(nèi)容緩存,用于提取代碼片段

    def parse_files(self):
        """遍歷目錄下所有 .c/.cpp/.h/.hpp 文件,解析并收集符號(hào)定義和引用關(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):
        """解析單個(gè)文件,更新符號(hào)表和依賴關(guān)系"""
        tu = self.index.parse(filepath, args=['-x', 'c++', '-std=c++11'])  # 根據(jù)實(shí)際情況調(diào)整編譯參數(shù)
        if not tu:
            return

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

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

    def _visit_children(self, cursor, filepath):
        """遞歸遍歷 AST,收集定義和引用關(guān)系"""
        # 處理當(dāng)前節(jié)點(diǎn)
        kind = cursor.kind
        location = cursor.location
        if location.file and location.file.name != filepath:
            # 忽略來自頭文件的節(jié)點(diǎn),因?yàn)樵摴?jié)點(diǎn)會(huì)在解析包含它的源文件時(shí)被捕獲
            # 但為了確保不遺漏,我們?cè)试S跨文件收集,但需要避免重復(fù)
            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)的引用(僅當(dāng)當(dāng)前節(jié)點(diǎn)是函數(shù)定義時(shí))
        if kind == CursorKind.FUNCTION_DECL and cursor.is_definition():
            self._collect_references(cursor)

        # 繼續(xù)遍歷子節(jié)點(diǎn)
        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):
        """添加符號(hào)到符號(hào)表,記錄其代碼范圍"""
        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]
        # 行號(hào)從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é)點(diǎn)
        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é)點(diǎn)
            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):
        """通過預(yù)處理令牌收集宏定義"""
        # 獲取所有預(yù)處理令牌
        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  # 這里需要復(fù)雜的狀態(tài)機(jī),省略實(shí)現(xiàn)細(xì)節(jié)
        # 實(shí)際上 libclang 提供了 CursorKind.MACRO_DEFINITION,但需要遍歷預(yù)處理實(shí)體?
        # 簡單方法:遍歷所有宏定義光標(biāo)
        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):
        """從目標(biāo)函數(shù)出發(fā),收集所有依賴的符號(hào)(廣度優(yōu)先)"""
        if self.target_func not in self.symbols:
            print(f"錯(cuò)誤:未找到函數(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)
            # 獲取該符號(hào)的依賴
            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):
        """對(duì)符號(hào)集合進(jìn)行拓?fù)渑判颍ū灰蕾嚨南容敵觯?""
        # 構(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)

        # 計(jì)算入度
        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 文件"""
        # 獲取依賴的所有符號(hào)
        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']

        # 拓?fù)渑判颍侯愋秃妥兞恐g可能有依賴,但簡單起見,我們先輸出類型,再輸出變量,再輸出函數(shù)
        # 更精確的做法是對(duì)所有符號(hào)一起拓?fù)渑判?
        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")

            # 按照拓?fù)漤樞蜉敵龃a片段
            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)} 個(gè)符號(hào)。")

def main():
    parser = argparse.ArgumentParser(description='提取 C/C++ 函數(shù)及其依賴,生成 standalone.cpp')
    parser.add_argument('directory', help='源代碼根目錄')
    parser.add_argument('function', help='目標(biāo)函數(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 不在默認(rèn)路徑,需要在代碼開頭設(shè)置 Config.set_library_path()

2.運(yùn)行

python extract.py /path/to/project my_function

3.輸出:生成 standalone.cpp,包含目標(biāo)函數(shù)及其依賴的所有函數(shù)、全局變量、類型定義和宏定義,按拓?fù)漤樞蚺帕校ū灰蕾嚨脑谇埃?/p>

注意事項(xiàng)

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

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

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

相關(guān)文章

最新評(píng)論

育儿| 吉安县| 秦皇岛市| 塔河县| 察隅县| 抚顺县| 安龙县| 义马市| 天门市| 南皮县| 凉山| 奉新县| 苍溪县| 修文县| 太保市| 桃源县| 大关县| 曲靖市| 中西区| 岑溪市| 马尔康县| 两当县| 嘉黎县| 安远县| 林甸县| 武强县| 余干县| 侯马市| 隆尧县| 靖边县| 开远市| 安福县| 尼木县| 扶余县| 顺平县| 廉江市| 兴宁市| 浦江县| 霍林郭勒市| 鹤峰县| 湄潭县|