You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
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