coverage-tool 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. coverage_tool/__init__.py +7 -0
  2. coverage_tool/analyzers/__init__.py +14 -0
  3. coverage_tool/analyzers/dependency.py +513 -0
  4. coverage_tool/converters/__init__.py +60 -0
  5. coverage_tool/converters/base.py +83 -0
  6. coverage_tool/converters/cunit_converter.py +47 -0
  7. coverage_tool/converters/factory.py +36 -0
  8. coverage_tool/converters/generic_converter.py +62 -0
  9. coverage_tool/converters/go_converter.py +178 -0
  10. coverage_tool/converters/gtest_converter.py +63 -0
  11. coverage_tool/core/__init__.py +18 -0
  12. coverage_tool/core/config.py +66 -0
  13. coverage_tool/core/reporter.py +270 -0
  14. coverage_tool/example.py +102 -0
  15. coverage_tool/handlers/__init__.py +13 -0
  16. coverage_tool/handlers/compilation.py +322 -0
  17. coverage_tool/handlers/env_checker.py +312 -0
  18. coverage_tool/main.py +559 -0
  19. coverage_tool/parsers/__init__.py +15 -0
  20. coverage_tool/parsers/junit.py +172 -0
  21. coverage_tool/runners/__init__.py +26 -0
  22. coverage_tool/runners/base.py +249 -0
  23. coverage_tool/runners/c_runner.py +66 -0
  24. coverage_tool/runners/cpp_runner.py +65 -0
  25. coverage_tool/runners/factory.py +41 -0
  26. coverage_tool/runners/go_runner.py +158 -0
  27. coverage_tool/runners/java_runner.py +54 -0
  28. coverage_tool/runners/python_runner.py +99 -0
  29. coverage_tool/test_removers/__init__.py +38 -0
  30. coverage_tool/test_removers/base.py +233 -0
  31. coverage_tool/test_removers/c_remover.py +210 -0
  32. coverage_tool/test_removers/cpp_remover.py +272 -0
  33. coverage_tool/test_removers/factory.py +45 -0
  34. coverage_tool/test_removers/go_remover.py +112 -0
  35. coverage_tool/test_removers/java_remover.py +216 -0
  36. coverage_tool/test_removers/python_remover.py +172 -0
  37. coverage_tool/utils/__init__.py +23 -0
  38. coverage_tool/utils/dependency_graph.py +52 -0
  39. coverage_tool/utils/file_backup.py +112 -0
  40. coverage_tool/utils/helpers.py +89 -0
  41. coverage_tool/utils/logger.py +54 -0
  42. coverage_tool/utils/progress.py +41 -0
  43. coverage_tool-1.0.0.dist-info/METADATA +484 -0
  44. coverage_tool-1.0.0.dist-info/RECORD +47 -0
  45. coverage_tool-1.0.0.dist-info/WHEEL +5 -0
  46. coverage_tool-1.0.0.dist-info/entry_points.txt +2 -0
  47. coverage_tool-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 智能编译失败处理器
4
+ 支持渐进式删除策略和依赖分析
5
+ """
6
+
7
+ import os
8
+ import re
9
+ import shutil
10
+ from typing import List, Tuple, Optional, Dict, Set
11
+ from pathlib import Path
12
+ from dataclasses import dataclass, field
13
+ from enum import Enum
14
+
15
+ from coverage_tool.utils import FileBackupManager, DependencyGraph, Logger, parse_error_locations, safe_delete_file
16
+
17
+
18
+ class FilePriority(Enum):
19
+ """文件优先级"""
20
+ LOW = 1 # 可以安全删除的文件(如测试文件、示例文件)
21
+ MEDIUM = 2 # 普通源文件
22
+ HIGH = 3 # 核心文件(如main文件、配置文件)
23
+ CRITICAL = 4 # 绝对不能删除的文件
24
+
25
+
26
+ @dataclass
27
+ class FileInfo:
28
+ """文件信息"""
29
+ path: str
30
+ priority: FilePriority
31
+ error_count: int = 0
32
+ is_test_file: bool = False
33
+ is_source_file: bool = False
34
+ dependents: List[str] = field(default_factory=list)
35
+
36
+
37
+ class SmartCompilationHandler:
38
+ """智能编译失败处理器"""
39
+
40
+ def __init__(self, target_dir: str, logger: Optional[Logger] = None):
41
+ self.target_dir = target_dir
42
+ self.logger = logger or Logger()
43
+ self.backup_manager = FileBackupManager()
44
+ self.dependency_graph = DependencyGraph()
45
+ self.deleted_files: List[str] = []
46
+ self.compilation_history: List[Dict] = []
47
+
48
+ # 文件优先级模式
49
+ self.priority_patterns = {
50
+ FilePriority.LOW: [
51
+ r'.*test.*\.(py|java|go|c|cpp|h|hpp)$',
52
+ r'.*example.*\.(py|java|go|c|cpp)$',
53
+ r'.*demo.*\.(py|java|go|c|cpp)$',
54
+ r'.*mock.*\.(py|java|go|c|cpp)$',
55
+ r'.*stub.*\.(py|java|go|c|cpp)$',
56
+ ],
57
+ FilePriority.HIGH: [
58
+ r'^main\.(py|java|go|c|cpp)$',
59
+ r'^index\.(py|java|go|c|cpp)$',
60
+ r'^app\.(py|java|go|c|cpp)$',
61
+ r'^setup\.py$',
62
+ r'^[Rr]eadme\.md$',
63
+ r'^go\.mod$',
64
+ r'^pom\.xml$',
65
+ r'^build\.gradle$',
66
+ r'^Makefile$',
67
+ r'^CMakeLists\.txt$',
68
+ ],
69
+ FilePriority.CRITICAL: [
70
+ r'.*\.git.*',
71
+ r'.*\.svn.*',
72
+ ]
73
+ }
74
+
75
+ def analyze_compilation_errors(self, errors: List[str]) -> List[FileInfo]:
76
+ """分析编译错误,提取问题文件"""
77
+ error_locations = parse_error_locations(errors, self.target_dir)
78
+
79
+ file_infos = []
80
+ for file_path, line_nums in error_locations.items():
81
+ if os.path.exists(file_path):
82
+ info = FileInfo(
83
+ path=file_path,
84
+ priority=self._determine_priority(file_path),
85
+ error_count=len(line_nums),
86
+ is_test_file=self._is_test_file(file_path),
87
+ is_source_file=self._is_source_file(file_path)
88
+ )
89
+ file_infos.append(info)
90
+
91
+ # 按优先级和错误数量排序(优先级低的先删除)
92
+ file_infos.sort(key=lambda x: (x.priority.value, -x.error_count))
93
+
94
+ return file_infos
95
+
96
+ def _determine_priority(self, file_path: str) -> FilePriority:
97
+ """确定文件优先级"""
98
+ file_name = os.path.basename(file_path)
99
+
100
+ # 检查CRITICAL模式
101
+ for pattern in self.priority_patterns[FilePriority.CRITICAL]:
102
+ if re.match(pattern, file_name, re.IGNORECASE):
103
+ return FilePriority.CRITICAL
104
+
105
+ # 检查HIGH模式
106
+ for pattern in self.priority_patterns[FilePriority.HIGH]:
107
+ if re.match(pattern, file_name, re.IGNORECASE):
108
+ return FilePriority.HIGH
109
+
110
+ # 检查LOW模式
111
+ for pattern in self.priority_patterns[FilePriority.LOW]:
112
+ if re.match(pattern, file_name, re.IGNORECASE):
113
+ return FilePriority.LOW
114
+
115
+ return FilePriority.MEDIUM
116
+
117
+ def _is_test_file(self, file_path: str) -> bool:
118
+ """检查是否是测试文件"""
119
+ test_patterns = [
120
+ r'.*test.*\.(py|java|go|c|cpp)$',
121
+ r'.*_test\.(py|java|go|c|cpp)$',
122
+ r'test_.*\.py$',
123
+ r'.*Test\.java$',
124
+ ]
125
+ file_name = os.path.basename(file_path)
126
+ for pattern in test_patterns:
127
+ if re.match(pattern, file_name, re.IGNORECASE):
128
+ return True
129
+ return False
130
+
131
+ def _is_source_file(self, file_path: str) -> bool:
132
+ """检查是否是源文件"""
133
+ source_extensions = ['.py', '.java', '.go', '.c', '.cpp', '.h', '.hpp']
134
+ ext = os.path.splitext(file_path)[1].lower()
135
+ return ext in source_extensions
136
+
137
+ def handle_compilation_failure(
138
+ self,
139
+ errors: List[str],
140
+ compile_func,
141
+ max_iterations: int = 10
142
+ ) -> Tuple[bool, List[str], List[str]]:
143
+ """
144
+ 处理编译失败,使用渐进式删除策略
145
+
146
+ Args:
147
+ errors: 编译错误列表
148
+ compile_func: 编译函数,接收target_dir,返回(success, errors)
149
+ max_iterations: 最大尝试次数
150
+
151
+ Returns:
152
+ (编译是否成功, 被删除的文件列表, 剩余的编译错误)
153
+ """
154
+ self.logger.info(f"开始处理编译失败,共 {len(errors)} 个错误")
155
+
156
+ iteration = 0
157
+ current_errors = errors.copy()
158
+
159
+ while iteration < max_iterations and current_errors:
160
+ iteration += 1
161
+ self.logger.info(f"编译修复迭代 {iteration}/{max_iterations}")
162
+
163
+ # 分析错误
164
+ file_infos = self.analyze_compilation_errors(current_errors)
165
+
166
+ if not file_infos:
167
+ self.logger.warning("无法从错误信息中识别问题文件")
168
+ break
169
+
170
+ # 过滤掉不能删除的文件
171
+ deletable_files = [
172
+ info for info in file_infos
173
+ if info.priority not in [FilePriority.CRITICAL, FilePriority.HIGH]
174
+ ]
175
+
176
+ if not deletable_files:
177
+ self.logger.warning("没有可安全删除的文件")
178
+ break
179
+
180
+ # 选择要删除的文件(每次最多删除3个,避免过度删除)
181
+ files_to_delete = deletable_files[:3]
182
+
183
+ # 检查依赖关系
184
+ for file_info in files_to_delete:
185
+ impacted = self.dependency_graph.get_impact_files(file_info.path)
186
+ if impacted:
187
+ self.logger.warning(
188
+ f"删除 {file_info.path} 会影响 {len(impacted)} 个文件"
189
+ )
190
+
191
+ # 备份并删除文件
192
+ deleted_in_iteration = []
193
+ for file_info in files_to_delete:
194
+ if safe_delete_file(file_info.path, self.backup_manager):
195
+ deleted_in_iteration.append(file_info.path)
196
+ self.deleted_files.append(file_info.path)
197
+ self.logger.info(f"已删除文件: {file_info.path}")
198
+
199
+ if not deleted_in_iteration:
200
+ self.logger.warning("本轮未能删除任何文件")
201
+ break
202
+
203
+ # 记录编译历史
204
+ self.compilation_history.append({
205
+ 'iteration': iteration,
206
+ 'deleted_files': deleted_in_iteration,
207
+ 'error_count_before': len(current_errors)
208
+ })
209
+
210
+ # 尝试重新编译
211
+ success, new_errors = compile_func(self.target_dir)
212
+
213
+ if success:
214
+ self.logger.info(f"编译成功!共进行了 {iteration} 轮修复")
215
+ return True, self.deleted_files, []
216
+
217
+ # 检查错误是否减少
218
+ if len(new_errors) >= len(current_errors):
219
+ self.logger.warning(
220
+ f"错误数量未减少 ({len(current_errors)} -> {len(new_errors)}),"
221
+ "可能需要更激进的删除策略"
222
+ )
223
+
224
+ current_errors = new_errors
225
+
226
+ # 达到最大迭代次数仍未成功
227
+ self.logger.error(f"编译修复失败,已达到最大迭代次数 {max_iterations}")
228
+ return False, self.deleted_files, current_errors
229
+
230
+ def restore_deleted_files(self) -> List[str]:
231
+ """恢复所有已删除的文件"""
232
+ restored = []
233
+ for file_path in self.deleted_files:
234
+ if self.backup_manager.restore_file(file_path):
235
+ restored.append(file_path)
236
+ self.logger.info(f"已恢复文件: {file_path}")
237
+ self.deleted_files.clear()
238
+ return restored
239
+
240
+ def get_deleted_files(self) -> List[str]:
241
+ """获取已删除的文件列表"""
242
+ return self.deleted_files.copy()
243
+
244
+ def get_compilation_history(self) -> List[Dict]:
245
+ """获取编译历史记录"""
246
+ return self.compilation_history.copy()
247
+
248
+ def cleanup(self):
249
+ """清理资源"""
250
+ self.backup_manager.cleanup()
251
+
252
+
253
+ class CompilationErrorParser:
254
+ """编译错误解析器"""
255
+
256
+ # 各种语言的编译错误模式
257
+ ERROR_PATTERNS = {
258
+ 'python': [
259
+ (r'File "([^"]+\.py)", line (\d+)', 'file_and_line'),
260
+ (r'SyntaxError.*in ([^\s]+\.py)', 'syntax_error'),
261
+ (r'ImportError.*from ([^\s]+\.py)', 'import_error'),
262
+ ],
263
+ 'java': [
264
+ (r'([^\s]+\.java):(\d+):', 'file_and_line'),
265
+ (r'error: cannot find symbol\s+Location: ([^\s]+\.java)', 'symbol_error'),
266
+ (r'error: package ([^\s]+) does not exist', 'package_error'),
267
+ ],
268
+ 'go': [
269
+ (r'# ([^\s]+)', 'package_error'),
270
+ (r'([^\s]+\.go):(\d+):(\d+):', 'file_and_line'),
271
+ (r'([^\s]+\.go):(\d+):', 'file_and_line'),
272
+ (r'undefined:\s+(\w+)', 'undefined_error'),
273
+ ],
274
+ 'c': [
275
+ (r'([^\s]+\.(c|h)):(\d+):(\d+):', 'file_and_line'),
276
+ (r'([^\s]+\.(c|h)):(\d+):', 'file_and_line'),
277
+ (r'undefined reference to', 'link_error'),
278
+ ],
279
+ 'cpp': [
280
+ (r'([^\s]+\.(cpp|hpp|cc|hh)):(\d+):(\d+):', 'file_and_line'),
281
+ (r'([^\s]+\.(cpp|hpp|cc|hh)):(\d+):', 'file_and_line'),
282
+ (r'undefined reference to', 'link_error'),
283
+ ]
284
+ }
285
+
286
+ @staticmethod
287
+ def parse_errors(errors: List[str], language: str, project_dir: str) -> Dict[str, List[Dict]]:
288
+ """
289
+ 解析编译错误
290
+
291
+ Returns:
292
+ 按文件分类的错误信息
293
+ """
294
+ patterns = CompilationErrorParser.ERROR_PATTERNS.get(language, [])
295
+ file_errors: Dict[str, List[Dict]] = {}
296
+
297
+ for error in errors:
298
+ for pattern, error_type in patterns:
299
+ matches = re.findall(pattern, error)
300
+ for match in matches:
301
+ if isinstance(match, tuple):
302
+ file_path = match[0]
303
+ line_num = int(match[1]) if len(match) > 1 and match[1].isdigit() else 0
304
+ else:
305
+ file_path = match
306
+ line_num = 0
307
+
308
+ # 规范化路径
309
+ if not os.path.isabs(file_path):
310
+ file_path = os.path.join(project_dir, file_path)
311
+ file_path = os.path.normpath(file_path)
312
+
313
+ if file_path not in file_errors:
314
+ file_errors[file_path] = []
315
+
316
+ file_errors[file_path].append({
317
+ 'line': line_num,
318
+ 'type': error_type,
319
+ 'message': error
320
+ })
321
+
322
+ return file_errors
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 环境检查器
4
+ 检查各语言编译和测试环境是否满足要求
5
+ 支持根据项目配置文件智能分析依赖
6
+ """
7
+
8
+ import subprocess
9
+ import shutil
10
+ import os
11
+ import re
12
+ from dataclasses import dataclass, field
13
+ from typing import List, Dict, Optional, Tuple
14
+ from enum import Enum
15
+ from pathlib import Path
16
+
17
+ class EnvironmentStatus(Enum):
18
+ OK = "ok"
19
+ MISSING = "missing"
20
+ WARNING = "warning"
21
+ OPTIONAL = "optional"
22
+
23
+ @dataclass
24
+ class EnvironmentCheck:
25
+ """环境检查项"""
26
+ name: str
27
+ status: EnvironmentStatus
28
+ version: Optional[str] = None
29
+ message: str = ""
30
+ suggestion: str = ""
31
+ required_by_project: bool = False
32
+
33
+ @dataclass
34
+ class EnvironmentReport:
35
+ """环境检查报告"""
36
+ language: str
37
+ checks: List[EnvironmentCheck] = field(default_factory=list)
38
+ all_passed: bool = True
39
+ critical_missing: List[str] = field(default_factory=list)
40
+ installation_suggestions: List[str] = field(default_factory=list)
41
+ project_config_detected: List[str] = field(default_factory=list)
42
+
43
+ def add_check(self, check: EnvironmentCheck):
44
+ self.checks.append(check)
45
+ if check.status == EnvironmentStatus.MISSING and check.required_by_project:
46
+ self.all_passed = False
47
+ self.critical_missing.append(check.name)
48
+
49
+ class EnvironmentChecker:
50
+ """环境检查器"""
51
+
52
+ def __init__(self):
53
+ self.check_results: Dict[str, EnvironmentReport] = {}
54
+
55
+ def check_language_environment(self, language: str, project_dir: str = None) -> EnvironmentReport:
56
+ """检查特定语言的环境"""
57
+ report = EnvironmentReport(language=language)
58
+
59
+ # 分析项目依赖
60
+ if project_dir and os.path.exists(project_dir):
61
+ self._analyze_project_dependencies(report, language, project_dir)
62
+
63
+ if language == "python":
64
+ self._check_python_env(report)
65
+ elif language == "java":
66
+ self._check_java_env(report)
67
+ elif language == "go":
68
+ self._check_go_env(report)
69
+ elif language in ["c", "cpp"]:
70
+ self._check_c_cpp_env(report)
71
+
72
+ return report
73
+
74
+ def _analyze_project_dependencies(self, report: EnvironmentReport, language: str, project_dir: str):
75
+ """分析项目配置文件"""
76
+ from coverage_tool.analyzers import DependencyAnalyzer
77
+
78
+ analyzer = DependencyAnalyzer(project_dir)
79
+ analysis = analyzer.analyze(language)
80
+
81
+ if analysis is None:
82
+ return
83
+
84
+ # 记录检测到的配置文件
85
+ if analysis.build_system:
86
+ report.project_config_detected.append(f"构建系统: {analysis.build_system}")
87
+ if analysis.test_framework:
88
+ report.project_config_detected.append(f"测试框架: {analysis.test_framework}")
89
+ if analysis.warnings:
90
+ for warning in analysis.warnings:
91
+ report.project_config_detected.append(f"⚠ {warning}")
92
+
93
+ # 检查项目依赖是否已安装
94
+ for dep in analysis.dependencies:
95
+ if dep.required or dep.installed:
96
+ check = EnvironmentCheck(
97
+ name=dep.name,
98
+ status=EnvironmentStatus.OK if dep.installed else EnvironmentStatus.MISSING,
99
+ version=dep.installed_version or dep.version or "",
100
+ message=f"{'已安装' if dep.installed else '未安装'}",
101
+ required_by_project=dep.required,
102
+ suggestion=self._get_suggestion(dep.name, language)
103
+ )
104
+ report.add_check(check)
105
+
106
+ def _get_suggestion(self, dep_name: str, language: str) -> str:
107
+ """获取安装建议"""
108
+ suggestions = {
109
+ 'pytest': 'pip install pytest',
110
+ 'coverage': 'pip install coverage',
111
+ 'tox': 'pip install tox',
112
+ 'unittest': 'Python标准库,无需安装',
113
+ 'maven': 'apt install maven (Linux) / brew install maven (macOS)',
114
+ 'gradle': 'https://gradle.org/install/',
115
+ 'java': 'https://adoptium.net/ 或 https://www.oracle.com/java/',
116
+ 'go': 'https://go.dev/dl/',
117
+ 'gcc': 'apt install gcc (Linux) / brew install gcc (macOS)',
118
+ 'g++': 'apt install g++ (Linux) / brew install gcc (macOS)',
119
+ 'make': 'apt install make (Linux) / brew install make (macOS)',
120
+ 'cmake': 'https://cmake.org/download/',
121
+ 'gcov': '随GCC安装',
122
+ 'lcov': 'apt install lcov (Linux)',
123
+ 'googletest': 'apt install libgtest-dev cmake',
124
+ }
125
+ return suggestions.get(dep_name, f'请安装 {dep_name}')
126
+
127
+ def _run_command(self, cmd: str) -> Tuple[bool, str, str]:
128
+ """运行命令检查"""
129
+ try:
130
+ result = subprocess.run(
131
+ cmd,
132
+ shell=True,
133
+ capture_output=True,
134
+ text=True,
135
+ timeout=10
136
+ )
137
+ return result.returncode == 0, result.stdout, result.stderr
138
+ except subprocess.TimeoutExpired:
139
+ return False, "", "命令执行超时"
140
+ except Exception as e:
141
+ return False, "", str(e)
142
+
143
+ def _check_python_env(self, report: EnvironmentReport):
144
+ """检查Python环境"""
145
+ check = EnvironmentCheck(
146
+ name="Python",
147
+ status=EnvironmentStatus.OK,
148
+ version="",
149
+ message="Python 环境正常",
150
+ required_by_project=True
151
+ )
152
+ success, stdout, _ = self._run_command("python3 --version")
153
+ if success:
154
+ check.version = stdout.strip()
155
+ check.message = f"Python 版本: {check.version}"
156
+ else:
157
+ check.status = EnvironmentStatus.MISSING
158
+ check.message = "未找到 Python"
159
+ check.suggestion = "请安装 Python 3.7+: https://www.python.org/downloads/"
160
+ report.add_check(check)
161
+
162
+ # 检查pytest
163
+ success, stdout, _ = self._run_command("python3 -m pytest --version")
164
+ check = EnvironmentCheck(
165
+ name="pytest",
166
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.WARNING,
167
+ message="pytest 未安装" if not success else f"pytest 版本: {stdout.strip().split(chr(10))[0] if stdout else '未知'}",
168
+ suggestion="pip install pytest" if not success else "",
169
+ required_by_project=True
170
+ )
171
+ if success:
172
+ check.version = stdout.strip().split('\n')[0]
173
+ report.add_check(check)
174
+
175
+ # 检查coverage
176
+ success, stdout, _ = self._run_command("python3 -m coverage --version")
177
+ check = EnvironmentCheck(
178
+ name="coverage.py",
179
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.WARNING,
180
+ message="coverage.py 未安装" if not success else f"coverage.py 已安装",
181
+ suggestion="pip install coverage" if not success else "",
182
+ required_by_project=False
183
+ )
184
+ if success:
185
+ check.version = stdout.strip().split('\n')[0]
186
+ report.add_check(check)
187
+
188
+ def _check_java_env(self, report: EnvironmentReport):
189
+ """检查Java环境"""
190
+ success, stdout, stderr = self._run_command("java -version 2>&1")
191
+ check = EnvironmentCheck(
192
+ name="Java JDK",
193
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.MISSING,
194
+ version="",
195
+ message="未找到 Java JDK" if not success else f"Java 版本: {(stdout or stderr).strip().split(chr(10))[0] if (stdout or stderr) else '未知'}",
196
+ suggestion="请安装 JDK 8+: https://adoptium.net/" if not success else "",
197
+ required_by_project=True
198
+ )
199
+ if success:
200
+ check.version = (stdout or stderr).strip().split('\n')[0]
201
+ report.add_check(check)
202
+
203
+ success, stdout, _ = self._run_command("mvn -version")
204
+ check = EnvironmentCheck(
205
+ name="Maven",
206
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.WARNING,
207
+ message="Maven 未安装" if not success else f"Maven 版本: {stdout.strip().split(chr(10))[0] if stdout else '未知'}",
208
+ suggestion="apt install maven / brew install maven" if not success else "",
209
+ required_by_project=False
210
+ )
211
+ if success:
212
+ check.version = stdout.strip().split('\n')[0]
213
+ report.add_check(check)
214
+
215
+ def _check_go_env(self, report: EnvironmentReport):
216
+ """检查Go环境"""
217
+ success, stdout, _ = self._run_command("go version")
218
+ check = EnvironmentCheck(
219
+ name="Go SDK",
220
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.MISSING,
221
+ version="",
222
+ message="未找到 Go SDK" if not success else f"Go 版本: {stdout.strip() if stdout else '未知'}",
223
+ suggestion="请安装 Go 1.16+: https://go.dev/dl/" if not success else "",
224
+ required_by_project=True
225
+ )
226
+ if success:
227
+ check.version = stdout.strip()
228
+ report.add_check(check)
229
+
230
+ def _check_c_cpp_env(self, report: EnvironmentReport):
231
+ """检查C/C++环境"""
232
+ success, stdout, _ = self._run_command("gcc --version")
233
+ check = EnvironmentCheck(
234
+ name="GCC",
235
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.MISSING,
236
+ version="",
237
+ message="GCC 未安装" if not success else f"GCC 版本: {stdout.strip().split(chr(10))[0] if stdout else '未知'}",
238
+ suggestion="apt install gcc (Linux) / brew install gcc (macOS)" if not success else "",
239
+ required_by_project=True
240
+ )
241
+ if success:
242
+ check.version = stdout.strip().split('\n')[0]
243
+ report.add_check(check)
244
+
245
+ success, stdout, _ = self._run_command("g++ --version")
246
+ check = EnvironmentCheck(
247
+ name="G++",
248
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.MISSING,
249
+ version="",
250
+ message="G++ 未安装" if not success else f"G++ 版本: {stdout.strip().split(chr(10))[0] if stdout else '未知'}",
251
+ suggestion="apt install g++ (Linux) / brew install gcc (macOS)" if not success else "",
252
+ required_by_project=True
253
+ )
254
+ if success:
255
+ check.version = stdout.strip().split('\n')[0]
256
+ report.add_check(check)
257
+
258
+ success, stdout, _ = self._run_command("make --version")
259
+ check = EnvironmentCheck(
260
+ name="Make",
261
+ status=EnvironmentStatus.OK if success else EnvironmentStatus.MISSING,
262
+ version="",
263
+ message="Make 未安装" if not success else f"Make 版本: {stdout.strip().split(chr(10))[0] if stdout else '未知'}",
264
+ suggestion="apt install make (Linux) / brew install make (macOS)" if not success else "",
265
+ required_by_project=True
266
+ )
267
+ if success:
268
+ check.version = stdout.strip().split('\n')[0]
269
+ report.add_check(check)
270
+
271
+ def print_report(self, report: EnvironmentReport):
272
+ """打印环境检查报告"""
273
+ print(f"\n{'='*60}")
274
+ print(f"环境检查报告: {report.language}")
275
+ print('='*60)
276
+
277
+ # 显示检测到的项目配置
278
+ if report.project_config_detected:
279
+ print("\n📁 检测到的项目配置:")
280
+ for config in report.project_config_detected:
281
+ print(f" • {config}")
282
+
283
+ for check in report.checks:
284
+ if check.status == EnvironmentStatus.OK:
285
+ status_icon = "✓"
286
+ elif check.status == EnvironmentStatus.WARNING:
287
+ status_icon = "⚠"
288
+ elif check.status == EnvironmentStatus.MISSING:
289
+ status_icon = "✗"
290
+ else:
291
+ status_icon = "○"
292
+
293
+ print(f"\n{status_icon} {check.name}")
294
+ if check.version:
295
+ print(f" 版本: {check.version}")
296
+ print(f" {check.message}")
297
+ if check.suggestion:
298
+ print(f" 安装: {check.suggestion}")
299
+
300
+ print(f"\n{'='*60}")
301
+ if report.all_passed:
302
+ print("✓ 环境检查通过")
303
+ else:
304
+ print("✗ 存在缺失的关键依赖,请先安装")
305
+ print(f"缺失项: {', '.join(report.critical_missing)}")
306
+ print('='*60)
307
+
308
+ def check_all_and_print(self, language: str, project_dir: str = None):
309
+ """检查并打印所有环境信息"""
310
+ report = self.check_language_environment(language, project_dir)
311
+ self.print_report(report)
312
+ return report