#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 宏使用情况依赖链分析工具 分析宏定义文件中哪些宏实际被外部使用,哪些仅在文件内部被引用 """ import re import os from pathlib import Path from typing import Dict, Set, List, Tuple class MacroAnalyzer: """宏依赖分析器""" def __init__(self, header_file: str, source_dir: str): self.header_file = Path(header_file) self.source_dir = Path(source_dir) self.macros: Dict[str, dict] = {} # 宏定义信息 self.dependencies: Dict[str, Set[str]] = {} # 宏依赖关系 self.external_usage: Dict[str, Set[str]] = {} # 外部使用情况 def parse_header(self): """解析头文件,提取所有宏定义""" with open(self.header_file, 'r', encoding='utf-8') as f: content = f.read() # 匹配 #define 宏定义 # 支持多行宏定义(使用 \ 续行) pattern = r'#define\s+(\w+)(?:\([^)]*\))?\s+((?:[^\n\\]|\\[\s\S])*)' for match in re.finditer(pattern, content): macro_name = match.group(1) macro_body = match.group(2).strip() # 移除续行符和多余空白 macro_body = re.sub(r'\\\s*\n\s*', ' ', macro_body) self.macros[macro_name] = { 'name': macro_name, 'body': macro_body, 'line': content[:match.start()].count('\n') + 1 } # 分析这个宏依赖了哪些其他宏 self.dependencies[macro_name] = self._find_dependencies(macro_body) def _find_dependencies(self, macro_body: str) -> Set[str]: """从宏定义体中找出依赖的其他宏""" deps = set() # 查找所有可能的宏引用(大写字母开头的标识符) for word in re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', macro_body): if word in self.macros or word.startswith('k') or word.startswith('K') or word.startswith('is'): deps.add(word) return deps def scan_external_usage(self): """扫描外部文件中宏的使用情况""" # 排除头文件本身和备份文件 exclude_files = { self.header_file.name, f"{self.header_file.name}.backup" } # 支持的文件扩展名 extensions = {'.m', '.mm', '.swift', '.h', '.c', '.cpp'} # 遍历所有源文件 for file_path in self.source_dir.rglob('*'): if file_path.is_file() and file_path.suffix in extensions: if file_path.name in exclude_files: continue try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # 检查每个宏是否在文件中使用 for macro_name in self.macros: # 使用单词边界匹配,避免部分匹配 pattern = r'\b' + re.escape(macro_name) + r'\b' if re.search(pattern, content): if macro_name not in self.external_usage: self.external_usage[macro_name] = set() # 存储相对路径 rel_path = file_path.relative_to(self.source_dir) self.external_usage[macro_name].add(str(rel_path)) except Exception as e: # 忽略无法读取的文件 pass def calculate_effective_usage(self) -> Dict[str, int]: """ 计算有效使用次数(依赖链分析) 如果一个宏只被文件内其他宏使用,但这些宏没有外部使用,则计为0 """ effective_usage = {} def is_effectively_used(macro_name: str, visited: Set[str] = None) -> bool: """递归检查宏是否被有效使用""" if visited is None: visited = set() # 防止循环依赖 if macro_name in visited: return False visited.add(macro_name) # 如果有外部使用,直接返回True if macro_name in self.external_usage: return True # 检查是否有其他宏依赖这个宏,且这些宏被有效使用 for other_macro, deps in self.dependencies.items(): if macro_name in deps: if is_effectively_used(other_macro, visited.copy()): return True return False # 计算每个宏的有效使用次数 for macro_name in self.macros: if is_effectively_used(macro_name): # 有效使用,统计外部使用次数 effective_usage[macro_name] = len(self.external_usage.get(macro_name, set())) else: # 无效使用 effective_usage[macro_name] = 0 return effective_usage def generate_report(self): """生成分析报告""" print("\n" + "=" * 80) print("宏使用情况依赖链分析报告") print("=" * 80) print(f"头文件: {self.header_file}") print(f"源码目录: {self.source_dir}") print(f"总宏定义数: {len(self.macros)}") print("=" * 80 + "\n") # 计算有效使用 effective_usage = self.calculate_effective_usage() # 分类统计 unused_macros = [] internal_only = [] external_used = [] for macro_name, count in sorted(effective_usage.items()): if count == 0: unused_macros.append(macro_name) elif macro_name in self.external_usage: external_used.append((macro_name, count)) else: internal_only.append(macro_name) # 输出未使用的宏 print("❌ 完全未使用的宏(包括依赖链分析)") print("-" * 80) if unused_macros: for macro_name in unused_macros: macro_info = self.macros[macro_name] # 检查是否被内部引用 referenced_by = [] for other, deps in self.dependencies.items(): if macro_name in deps and other != macro_name: referenced_by.append(other) status = "" if referenced_by: status = f" (被内部宏引用: {', '.join(referenced_by)},但这些宏未被外部使用)" print(f" • {macro_name}{status}") print(f" 行号: {macro_info['line']}") print(f" 定义: {macro_info['body'][:60]}...") print() else: print(" 无\n") # 输出仅内部使用的宏(有效) print("✅ 仅被内部其他宏使用(但这些宏有外部使用)") print("-" * 80) if internal_only: for macro_name in internal_only: macro_info = self.macros[macro_name] # 找出哪些外部使用的宏依赖它 used_by_external = [] def find_external_users(macro: str, visited: Set[str] = None) -> List[str]: if visited is None: visited = set() if macro in visited: return [] visited.add(macro) users = [] for other, deps in self.dependencies.items(): if macro in deps: if other in self.external_usage: users.append(other) else: users.extend(find_external_users(other, visited)) return users used_by_external = find_external_users(macro_name) print(f" • {macro_name}") print(f" 被以下外部使用的宏依赖: {', '.join(set(used_by_external)) if used_by_external else '(间接)'}") print() else: print(" 无\n") # 输出有外部使用的宏 print("✅ 有外部使用的宏") print("-" * 80) if external_used: for macro_name, count in sorted(external_used, key=lambda x: x[1], reverse=True): files = self.external_usage[macro_name] print(f" • {macro_name}") print(f" 外部使用次数: {count} 个文件") if count <= 3: for f in list(files)[:3]: print(f" - {f}") print() else: print(" 无\n") # 统计汇总 print("\n" + "=" * 80) print("📊 统计汇总") print("=" * 80) print(f"❌ 未使用宏: {len(unused_macros)} 个 ({len(unused_macros)/len(self.macros)*100:.1f}%)") print(f"⚙️ 内部使用宏: {len(internal_only)} 个 ({len(internal_only)/len(self.macros)*100:.1f}%)") print(f"✅ 外部使用宏: {len(external_used)} 个 ({len(external_used)/len(self.macros)*100:.1f}%)") print("=" * 80 + "\n") # 建议清理 if unused_macros: print("💡 建议清理以下宏定义:") print("-" * 80) for macro_name in unused_macros: macro_info = self.macros[macro_name] print(f" 第 {macro_info['line']} 行: #define {macro_name} ...") print() def main(): import sys if len(sys.argv) < 3: print("用法: python3 analyze_macro_usage.py <头文件路径> <源码目录>") print("示例: python3 analyze_macro_usage.py YuMi/Global/YUMIMacroUitls.h YuMi/") sys.exit(1) header_file = sys.argv[1] source_dir = sys.argv[2] if not os.path.exists(header_file): print(f"错误: 头文件不存在: {header_file}") sys.exit(1) if not os.path.exists(source_dir): print(f"错误: 源码目录不存在: {source_dir}") sys.exit(1) analyzer = MacroAnalyzer(header_file, source_dir) print("正在解析头文件...") analyzer.parse_header() print(f"正在扫描外部文件使用情况...") analyzer.scan_external_usage() print("正在分析依赖链...") analyzer.generate_report() if __name__ == '__main__': main()