""" Cppcheck 运行和报告生成模块 """ import subprocess import shutil from pathlib import Path from typing import Optional, List def find_cppcheck_executable() -> Optional[str]: """查找 cppcheck 可执行文件""" # 首先检查系统 PATH cppcheck_path = shutil.which("cppcheck") if cppcheck_path: return cppcheck_path # 检查常见安装位置 common_paths = [ "/usr/bin/cppcheck", "/usr/local/bin/cppcheck", "/opt/cppcheck/bin/cppcheck", ] for path in common_paths: if Path(path).exists(): return path return None def run_cppcheck( target: Path, output_xml: Path, enable_all: bool = True, std: Optional[str] = None, include_dirs: Optional[List[str]] = None, suppress: Optional[List[str]] = None, timeout: int = 300, cppcheck_path: Optional[str] = None ) -> dict: """ 运行 cppcheck 并生成 XML 报告 Args: target: 要检查的文件或目录路径 output_xml: 输出的 XML 报告路径 enable_all: 是否启用所有检查 std: C/C++ 标准(如 "c99", "c11", "c++17") include_dirs: 额外的头文件搜索路径列表 suppress: 要抑制的规则 ID 列表 timeout: 超时时间(秒) cppcheck_path: cppcheck 可执行文件路径(如果为 None,则自动查找) Returns: dict: 包含执行结果的字典 { "success": bool, "report_path": Path, "error": str, "stderr": str, "stdout": str } """ result = { "success": False, "report_path": output_xml, "error": "", "stderr": "", "stdout": "" } # 查找 cppcheck 可执行文件 if cppcheck_path is None: cppcheck_path = find_cppcheck_executable() if cppcheck_path is None: result["error"] = "未找到 cppcheck 可执行文件。请确保已安装 cppcheck 或在 PATH 中可用。" return result if not Path(cppcheck_path).exists(): result["error"] = f"cppcheck 可执行文件不存在: {cppcheck_path}" return result # 检查目标路径 if not target.exists(): result["error"] = f"目标路径不存在: {target}" return result # 确保输出目录存在 output_xml.parent.mkdir(parents=True, exist_ok=True) # 构建 cppcheck 命令 cmd = [cppcheck_path] if enable_all: cmd.append("--enable=all") if std: cmd.append(f"--std={std}") # 添加头文件搜索路径 if include_dirs: for include_dir in include_dirs: if Path(include_dir).exists(): cmd.extend(["-I", include_dir]) # 添加抑制规则 if suppress: for rule_id in suppress: cmd.extend(["--suppress", rule_id]) # XML 输出选项 cmd.extend(["--xml", "--xml-version=2"]) # 目标路径 cmd.append(str(target)) try: print(f"正在运行 cppcheck: {' '.join(cmd)}") print(f"目标: {target}") print(f"输出: {output_xml}") # 运行 cppcheck,将 stderr 重定向到 XML 文件(cppcheck 将错误输出到 stderr) with open(output_xml, 'w', encoding='utf-8') as f: process = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=f, # XML 输出到 stderr text=True, timeout=timeout, cwd=target.parent if target.is_file() else target ) result["stdout"] = process.stdout or "" result["success"] = True # 检查报告文件是否生成且非空 if output_xml.exists() and output_xml.stat().st_size > 0: print(f"✓ cppcheck 报告已生成: {output_xml}") print(f" 文件大小: {output_xml.stat().st_size} 字节") else: result["error"] = "cppcheck 报告文件为空或未生成" result["success"] = False except subprocess.TimeoutExpired: result["error"] = f"cppcheck 执行超时({timeout}秒)" except FileNotFoundError: result["error"] = f"未找到 cppcheck 可执行文件: {cppcheck_path}" except Exception as e: result["error"] = f"运行 cppcheck 时发生错误: {str(e)}" result["stderr"] = str(e) return result def auto_detect_c_standard(target: Path) -> Optional[str]: """ 根据文件扩展名自动检测 C/C++ 标准 Returns: "c99" 或 "c++17" 或 None """ if target.is_file(): suffix = target.suffix.lower() if suffix in ['.c', '.h']: return "c99" elif suffix in ['.cpp', '.cc', '.cxx', '.hpp', '.hxx']: return "c++17" elif target.is_dir(): # 检查目录中的文件 c_files = list(target.glob("*.c")) + list(target.glob("*.h")) cpp_files = list(target.glob("*.cpp")) + list(target.glob("*.hpp")) if c_files and not cpp_files: return "c99" elif cpp_files: return "c++17" return None