openubmc-bingo 0.6.45__py3-none-any.whl → 0.6.99__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 (96) hide show
  1. bmcgo/__init__.py +1 -1
  2. bmcgo/bmcgo.py +9 -3
  3. bmcgo/bmcgo_config.py +16 -0
  4. bmcgo/cli/cli.py +72 -21
  5. bmcgo/codegen/__init__.py +1 -1
  6. bmcgo/codegen/lua/codegen.py +2 -2
  7. bmcgo/codegen/lua/script/check_intfs.py +1 -0
  8. bmcgo/codegen/lua/script/dto/options.py +1 -0
  9. bmcgo/codegen/lua/script/gen_db_json.py +4 -3
  10. bmcgo/codegen/lua/script/gen_rpc_msg_json.py +78 -11
  11. bmcgo/codegen/lua/script/model_consistency_check.py +1 -1
  12. bmcgo/codegen/lua/script/render_utils/db_lua.py +5 -6
  13. bmcgo/codegen/lua/script/render_utils/model_lua.py +5 -1
  14. bmcgo/codegen/lua/script/template.py +5 -0
  15. bmcgo/codegen/lua/script/utils.py +50 -8
  16. bmcgo/codegen/lua/templates/apps/Makefile +2 -2
  17. bmcgo/codegen/lua/templates/apps/client.lua.mako +1 -1
  18. bmcgo/codegen/lua/templates/apps/model.lua.mako +4 -3
  19. bmcgo/codegen/lua/templates/apps/service.lua.mako +1 -1
  20. bmcgo/codegen/lua/templates/apps/utils/mdb_intf.lua.mako +4 -0
  21. bmcgo/codegen/lua/templates/new_app_v2/CMakeLists.txt.mako +26 -0
  22. bmcgo/codegen/lua/templates/new_app_v2/conanfile.py.mako +9 -0
  23. bmcgo/codegen/lua/v1/script/render_utils/db_lua.py +5 -6
  24. bmcgo/codegen/lua/v1/script/render_utils/model_lua.py +13 -1
  25. bmcgo/codegen/lua/v1/templates/apps/client.lua.mako +1 -1
  26. bmcgo/codegen/lua/v1/templates/apps/local_db.lua.mako +0 -4
  27. bmcgo/codegen/lua/v1/templates/apps/message.lua.mako +3 -0
  28. bmcgo/codegen/lua/v1/templates/apps/model.lua.mako +3 -0
  29. bmcgo/codegen/lua/v1/templates/apps/utils/mdb_intf.lua.mako +6 -4
  30. bmcgo/component/analysis/analysis.py +9 -4
  31. bmcgo/component/analysis/dep-rules.json +20 -8
  32. bmcgo/component/analysis/dep_node.py +2 -0
  33. bmcgo/component/analysis/intf_validation.py +8 -7
  34. bmcgo/component/analysis/sr_validation.py +5 -4
  35. bmcgo/component/busctl_log_parse/busctl_log_parser.py +809 -0
  36. bmcgo/component/busctl_log_parse/mock_data_save.py +170 -0
  37. bmcgo/component/busctl_log_parse/test_data_save.py +49 -0
  38. bmcgo/component/component_helper.py +29 -0
  39. bmcgo/component/coverage/incremental_cov.py +5 -0
  40. bmcgo/component/fixture/__init__.py +29 -0
  41. bmcgo/component/fixture/auto_case_generator.py +490 -0
  42. bmcgo/component/fixture/busctl_type_converter.py +1081 -0
  43. bmcgo/component/fixture/common_config.py +15 -0
  44. bmcgo/component/fixture/dbus_gateway.py +669 -0
  45. bmcgo/component/fixture/dbus_library.py +250 -0
  46. bmcgo/component/fixture/dbus_mock_utils.py +514 -0
  47. bmcgo/component/fixture/dbus_response_handler.py +138 -0
  48. bmcgo/component/fixture/dbus_signature.py +110 -0
  49. bmcgo/component/template_v2/conanbase.py.mako +1 -5
  50. bmcgo/component/test.py +69 -10
  51. bmcgo/error_analyzer/__init__.py +0 -0
  52. bmcgo/error_analyzer/case_matcher.py +114 -0
  53. bmcgo/error_analyzer/log_parser.py +128 -0
  54. bmcgo/error_analyzer/unified_error_analyzer.py +359 -0
  55. bmcgo/error_cases/cases.yml +59 -0
  56. bmcgo/error_cases/cases_template_valid.json +71 -0
  57. bmcgo/error_cases/conanfile.py +58 -0
  58. bmcgo/frame.py +0 -4
  59. bmcgo/functional/analysis.py +18 -12
  60. bmcgo/functional/bmc_studio_action.py +21 -10
  61. bmcgo/functional/check.py +86 -42
  62. bmcgo/functional/conan_index_build.py +1 -1
  63. bmcgo/functional/config.py +22 -18
  64. bmcgo/functional/csr_build.py +63 -34
  65. bmcgo/functional/deploy.py +4 -3
  66. bmcgo/functional/diff.py +51 -34
  67. bmcgo/functional/full_component.py +16 -5
  68. bmcgo/functional/hpm_signer.py +484 -0
  69. bmcgo/functional/new.py +8 -2
  70. bmcgo/functional/schema_valid.py +111 -15
  71. bmcgo/functional/upgrade.py +6 -6
  72. bmcgo/misc.py +1 -0
  73. bmcgo/tasks/task_build_conan.py +27 -6
  74. bmcgo/tasks/task_build_rootfs_img.py +120 -83
  75. bmcgo/tasks/task_buildgppbin.py +30 -13
  76. bmcgo/tasks/task_buildhpm_ext4.py +5 -3
  77. bmcgo/tasks/task_download_buildtools.py +20 -11
  78. bmcgo/tasks/task_download_dependency.py +29 -20
  79. bmcgo/tasks/task_hpm_envir_prepare.py +32 -53
  80. bmcgo/tasks/task_packet_to_supporte.py +12 -4
  81. bmcgo/tasks/task_prepare.py +1 -1
  82. bmcgo/tasks/task_sign_and_pack_hpm.py +15 -7
  83. bmcgo/utils/component_version_check.py +4 -4
  84. bmcgo/utils/config.py +3 -0
  85. bmcgo/utils/fetch_component_code.py +148 -17
  86. bmcgo/utils/install_manager.py +2 -2
  87. bmcgo/utils/installations/base_installer.py +10 -27
  88. bmcgo/utils/installations/install_plans/studio.yml +3 -0
  89. bmcgo/utils/mapping_config_patch.py +5 -4
  90. bmcgo/utils/tools.py +49 -7
  91. {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/METADATA +1 -1
  92. {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/RECORD +95 -74
  93. bmcgo/tasks/download_buildtools_hm.py +0 -124
  94. {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/WHEEL +0 -0
  95. {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/entry_points.txt +0 -0
  96. {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/python3
2
+ # coding: utf-8
3
+ # Copyright (c) 2024 Huawei Technologies Co., Ltd.
4
+ # openUBMC is licensed under Mulan PSL v2.
5
+ # You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ # You may obtain a copy of Mulan PSL v2 at:
7
+ # http://license.coscl.org.cn/MulanPSL2
8
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9
+ # EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10
+ # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11
+ # See the Mulan PSL v2 for more details.
12
+
13
+ import os
14
+ import glob
15
+ import logging
16
+
17
+ from .log_parser import LogParser
18
+ from .case_matcher import CaseMatcher
19
+
20
+
21
+ class UnifiedErrorAnalyzer:
22
+ """统一的错误分析器 - 结合命令失败和日志分析"""
23
+
24
+ def __init__(self, cases):
25
+ self.log_parser = LogParser()
26
+ self.case_matcher = CaseMatcher()
27
+ self.cases = cases
28
+
29
+ @staticmethod
30
+ def _find_log_files_in_directory(directory, recursive=True):
31
+ """
32
+ 在目录中查找日志文件
33
+
34
+ Args:
35
+ directory: 目录路径
36
+ recursive: 是否递归查找子目录
37
+ """
38
+ log_files = []
39
+
40
+ if recursive:
41
+ # 递归查找所有 .log 文件
42
+ pattern = os.path.join(directory, "**", "*.log")
43
+ log_files = glob.glob(pattern, recursive=True)
44
+ else:
45
+ # 只查找当前目录下的 .log 文件
46
+ pattern = os.path.join(directory, "*.log")
47
+ log_files = glob.glob(pattern)
48
+
49
+ # 也可以查找其他常见的日志文件扩展名
50
+ additional_patterns = [
51
+ os.path.join(directory, "**", "*.log.*"), # 滚动日志文件
52
+ os.path.join(directory, "**", "*.txt"), # 文本日志文件
53
+ os.path.join(directory, "**", "*.err"), # 错误日志文件
54
+ ]
55
+
56
+ for pattern in additional_patterns:
57
+ log_files.extend(glob.glob(pattern, recursive=True))
58
+
59
+ # 去重并返回
60
+ return list(set(log_files))
61
+
62
+ @staticmethod
63
+ def _extract_error_code_from_failure(failure):
64
+ """从失败信息中提取错误代码"""
65
+ error = failure.get("error", "")
66
+ error_type = failure.get("error_type", "")
67
+
68
+ # 根据错误类型和内容生成错误代码
69
+ if "TimeoutExpired" in error_type:
70
+ return "COMMAND_TIMEOUT"
71
+ elif "CalledProcessError" in error_type:
72
+ return "COMMAND_FAILED"
73
+ elif "FileNotFoundError" in error_type:
74
+ return "COMMAND_NOT_FOUND"
75
+ elif "PermissionError" in error_type:
76
+ return "PERMISSION_DENIED"
77
+ else:
78
+ return "COMMAND_ERROR"
79
+
80
+ @staticmethod
81
+ def _format_failure_log(failure):
82
+ """格式化失败日志"""
83
+ parts = []
84
+
85
+ if failure.get("command_key"):
86
+ parts.append(f"[{failure['command_key']}]")
87
+
88
+ parts.append(failure["command_str"])
89
+ parts.append("[FAILED]")
90
+
91
+ exec_time = failure.get("execution_time", 0)
92
+ parts.append(f"[{exec_time:.2f}s]")
93
+
94
+ if "error" in failure:
95
+ parts.append(f"[ERROR: {failure['error']}]")
96
+
97
+ return " ".join(parts)
98
+
99
+ @staticmethod
100
+ def _deduplicate_cases(cases):
101
+ """案例去重"""
102
+ seen_signatures = set()
103
+ unique_cases = []
104
+
105
+ for case in cases:
106
+ # 创建更严格的案例签名
107
+ title = case.get("title", "")
108
+
109
+ # 使用原始日志内容(去除ANSI颜色码)进行去重
110
+ raw_log = case["log_data"].get("raw_log", "")
111
+
112
+ # 去除ANSI颜色码
113
+ import re
114
+
115
+ ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
116
+ clean_log = ansi_escape.sub("", raw_log).strip()
117
+
118
+ # 创建基于标题和清理后日志内容的签名
119
+ signature = f"{title}_{hash(clean_log)}"
120
+
121
+ if signature not in seen_signatures:
122
+ seen_signatures.add(signature)
123
+ unique_cases.append(case)
124
+
125
+ return unique_cases
126
+
127
+ @staticmethod
128
+ def _output_unified_case(case, case_number):
129
+ """输出统一格式的案例"""
130
+ source = case["log_data"].get("source", "unknown")
131
+ if source == "log_file":
132
+ source_icon = "📁"
133
+ source_text = "日志文件"
134
+ file_path = case["log_data"].get("file_path", "unknown")
135
+ file_name = file_path
136
+ else:
137
+ source_icon = "🔧"
138
+ source_text = "命令执行"
139
+ file_name = None
140
+
141
+ logging.info(
142
+ f"{source_icon} 案例 {case_number}: {case.get('title', '未命名案例')}"
143
+ )
144
+ logging.info("─" * 50)
145
+
146
+ logging.info(f" 🕐 发生时间: {case['log_data']['timestamp']}")
147
+ logging.info(f" 📍 来源: {source_text}")
148
+
149
+ if file_name:
150
+ logging.info(f" 📄 文件: {file_path}")
151
+
152
+ logging.info(f" 📝 问题描述: {case.get('description', '')}")
153
+
154
+ if case.get("steps"):
155
+ logging.info(" 👣 重现步骤:")
156
+ for j, step in enumerate(case.get("steps", []), 1):
157
+ logging.info(f" {j}. {step}")
158
+
159
+ logging.info(" 📄 相关输出:")
160
+ logging.info(f" {case['log_data']['raw_log']}")
161
+
162
+ # 如果是命令失败案例,显示额外信息
163
+ if source == "command_failure" and "command_data" in case:
164
+ cmd_data = case["command_data"]
165
+ logging.info(" 🔧 命令详情:")
166
+ logging.info(f" 命令: {cmd_data['command_str']}")
167
+ logging.info(f" 执行时间: {cmd_data['execution_time']:.2f}秒")
168
+ logging.info(f" 错误类型: {cmd_data.get('error_type', 'Unknown')}")
169
+
170
+ if case.get("solution"):
171
+ logging.info(f" 💡 解决方案: {case['solution']}")
172
+
173
+ logging.info("─" * 50)
174
+
175
+ def analyze_errors(self, log_sources, command_failures=None):
176
+ """
177
+ 统一分析错误:支持多种日志源
178
+
179
+ Args:
180
+ log_sources: 可以是以下类型:
181
+ - 单个文件路径 (str)
182
+ - 文件路径列表 (list)
183
+ - 文件夹路径 (str) - 会分析该文件夹下所有 .log 文件
184
+ command_failures: 命令失败信息列表
185
+ """
186
+ logging.info("\n" + "=" * 60)
187
+ logging.info("🔍 开始统一错误分析")
188
+ logging.info("=" * 60)
189
+
190
+ # 解析日志源
191
+ log_files = self._resolve_log_sources(log_sources)
192
+
193
+ if not log_files:
194
+ logging.warning("❌ 未找到任何日志文件")
195
+ return []
196
+
197
+ all_cases = []
198
+ all_log_entries = []
199
+
200
+ # 1. 分析所有日志文件
201
+ for log_file in log_files:
202
+ file_cases, file_entries = self._analyze_single_log_file(log_file)
203
+ all_cases.extend(file_cases)
204
+ all_log_entries.extend(file_entries)
205
+
206
+ # 2. 分析命令失败信息
207
+ if command_failures:
208
+ command_cases = self._analyze_command_failures(command_failures)
209
+ all_cases.extend(command_cases)
210
+
211
+ # 3. 合并和去重
212
+ unique_cases = self._deduplicate_cases(all_cases)
213
+
214
+ # 4. 输出分析结果
215
+ self._output_unified_analysis(
216
+ unique_cases, log_files, command_failures, all_log_entries
217
+ )
218
+
219
+ return unique_cases
220
+
221
+ def _resolve_log_sources(self, log_sources):
222
+ """
223
+ 解析日志源,返回文件路径列表
224
+
225
+ Args:
226
+ log_sources: 单个文件路径、文件列表或文件夹路径
227
+ """
228
+ if isinstance(log_sources, str):
229
+ # 单个路径
230
+ if os.path.isfile(log_sources):
231
+ # 单个文件
232
+ return [log_sources]
233
+ elif os.path.isdir(log_sources):
234
+ # 文件夹 - 查找所有 .log 文件
235
+ return self._find_log_files_in_directory(log_sources)
236
+ else:
237
+ # 可能是通配符模式
238
+ return glob.glob(log_sources)
239
+
240
+ elif isinstance(log_sources, list):
241
+ # 文件列表
242
+ all_files = []
243
+ for source in log_sources:
244
+ if os.path.isfile(source):
245
+ all_files.append(source)
246
+ elif os.path.isdir(source):
247
+ all_files.extend(self._find_log_files_in_directory(source))
248
+ else:
249
+ # 通配符模式
250
+ all_files.extend(glob.glob(source))
251
+ return all_files
252
+
253
+ else:
254
+ return []
255
+
256
+ def _analyze_single_log_file(self, log_file_path):
257
+ """分析单个日志文件"""
258
+ try:
259
+ log_entries = self.log_parser.parse_logs(log_file_path)
260
+ cases = self.cases
261
+ matched_cases = self.case_matcher.match_cases(log_entries, cases)
262
+
263
+ # 标记来源文件
264
+ for case in matched_cases:
265
+ case["log_data"]["source"] = "log_file"
266
+ case["log_data"]["file_path"] = log_file_path
267
+
268
+ return matched_cases, log_entries
269
+
270
+ except Exception as e:
271
+ logging.error(f" ❌ 分析文件失败 {log_file_path}: {e}")
272
+ return [], []
273
+
274
+ def _analyze_command_failures(self, command_failures):
275
+ """分析命令失败信息"""
276
+ cases = self.cases
277
+ matched_cases = []
278
+
279
+ for failure in command_failures:
280
+ # 将命令失败信息转换为日志格式进行分析
281
+ log_entry = self._convert_failure_to_log_entry(failure)
282
+
283
+ # 匹配案例
284
+ case = self.case_matcher.find_matching_case(log_entry, cases)
285
+ if case:
286
+ enriched_case = self.case_matcher.enrich_case_with_log(case, log_entry)
287
+ enriched_case["command_data"] = failure # 保留原始命令数据
288
+ matched_cases.append(enriched_case)
289
+
290
+ return matched_cases
291
+
292
+ def _convert_failure_to_log_entry(self, failure):
293
+ """将命令失败信息转换为日志条目格式"""
294
+ return {
295
+ "timestamp": failure["timestamp"],
296
+ "level": "ERROR",
297
+ "module": "CommandExecutor",
298
+ "message": f"Command failed: {failure['command_str']} - {failure['error']}",
299
+ "error_code": self._extract_error_code_from_failure(failure),
300
+ "parameters": {
301
+ "command": failure["command_str"],
302
+ "execution_time": failure["execution_time"],
303
+ "error_type": failure["error_type"],
304
+ },
305
+ "raw_line": self._format_failure_log(failure),
306
+ "source": "command_failure",
307
+ }
308
+
309
+ def _load_cases(self):
310
+ """加载案例模板"""
311
+ import yaml
312
+
313
+ try:
314
+ with open(self.case_file_path, "r", encoding="utf-8") as f:
315
+ data = yaml.safe_load(f)
316
+ return data.get("cases", [])
317
+ except Exception as e:
318
+ logging.error(f"❌ 加载案例文件失败: {e}")
319
+ return []
320
+
321
+ def _output_unified_analysis(
322
+ self, cases, log_files, command_failures, all_log_entries
323
+ ):
324
+ """输出统一分析结果"""
325
+ if not cases:
326
+ logging.warning("\n✅ 没有发现匹配的错误案例")
327
+ return
328
+
329
+ # 统计信息
330
+ log_cases = [c for c in cases if c["log_data"].get("source") == "log_file"]
331
+ command_cases = [
332
+ c
333
+ for c in cases
334
+ if c["log_data"].get("source") == "command_failure"
335
+ ]
336
+
337
+ # 按文件统计
338
+ file_stats = {}
339
+ for case in log_cases:
340
+ file_path = case["log_data"].get("file_path", "unknown")
341
+ file_stats[file_path] = file_stats.get(file_path, 0) + 1
342
+
343
+ logging.info(f" 📊 统一分析完成!")
344
+ logging.info(f" 分析文件数量: {len(log_files)} 个")
345
+ logging.info(f" 日志条目总数: {len(all_log_entries)} 条")
346
+ logging.info(f" 日志文件案例: {len(log_cases)} 个")
347
+ logging.info(f" 命令失败案例: {len(command_cases)} 个")
348
+ logging.info(f" 总案例: {len(cases)} 个")
349
+
350
+ if command_failures:
351
+ logging.info(f" 分析的命令失败: {len(command_failures)} 个")
352
+
353
+ # 输出所有案例
354
+ logging.info("\n" + "=" * 60)
355
+ logging.info("📋 详细错误分析")
356
+ logging.info("=" * 60)
357
+
358
+ for i, case in enumerate(cases, 1):
359
+ self._output_unified_case(case, i)
@@ -0,0 +1,59 @@
1
+ # yaml-language-server: $schema=cases_template_valid.json
2
+ version: 1.0.0
3
+ cases:
4
+ bingo_case_001:
5
+ title: 构建运行内存不足
6
+ description: 构建运行内存不足
7
+ matching_rules:
8
+ - field: message
9
+ operator: regex
10
+ value: internal compiler error
11
+ solution: 构建运行内存不足,请检查宿主机资源状态
12
+ bingo_case_002:
13
+ title: conan仓中不存在指定版本的组件
14
+ description: conan仓中不存在指定版本的组件
15
+ matching_rules:
16
+ - field: message
17
+ operator: regex
18
+ value: was not found in remote .*
19
+ solution: conan仓中不存在指定版本的组件,请检查组件是否发布
20
+ bingo_case_003:
21
+ title: 未设置对应的conan远程仓
22
+ description: 未设置对应的conan远程仓
23
+ matching_rules:
24
+ - field: message
25
+ operator: regex
26
+ value: No remote .* defined in remote
27
+ solution: 未设置对应的conan远程仓,请参考conan remote add xxx进行设置
28
+ bingo_case_004:
29
+ title: 环境未初始化
30
+ description: 环境未初始化
31
+ matching_rules:
32
+ - field: message
33
+ operator: regex
34
+ value: CMAKE_C_COMPILER not set, after EnableLanguage
35
+ solution: 请在manifest目录下执行python3 init.py初始化环境
36
+ bingo_case_005:
37
+ title: 无法下载git源码
38
+ description: 无法下载git源码
39
+ matching_rules:
40
+ - field: message
41
+ operator: regex
42
+ value: Couldn't checkout .* git
43
+ solution: 请检查本机git令牌配置,确保存在源码所需令牌
44
+ bingo_case_006:
45
+ title: 当前环境缺少待签名的uboot文件
46
+ description: 当前环境缺少待签名的uboot文件
47
+ matching_rules:
48
+ - field: message
49
+ operator: regex
50
+ value: 执行失败,打开了自签名模式但未找到待签名的uboot文件,构建失败
51
+ solution: 下载官网bmc_sdk,替换本地~/sdk, 确保带时间戳结尾的文件存在
52
+ bingo_case_007:
53
+ title: Linux权限错误
54
+ description: Linux权限错误
55
+ matching_rules:
56
+ - field: message
57
+ operator: regex
58
+ value: permission denied
59
+ solution: 请检查对应出错的文件权限,并确认当前用户是否有该操作权限
@@ -0,0 +1,71 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Bingo Cases Schema",
4
+ "description": "Schema for validating bingo cases configuration",
5
+ "type": "object",
6
+ "required": ["version", "cases"],
7
+ "properties": {
8
+ "version": {
9
+ "type": "string",
10
+ "description": "版本号,必须符合三段式版本格式",
11
+ "pattern": "^\\d+\\.\\d+\\.\\d+$"
12
+ },
13
+ "cases": {
14
+ "type": "object",
15
+ "description": "案例集合",
16
+ "minProperties": 1,
17
+ "patternProperties": {
18
+ "^bingo_case_[0-9]{3,}$": {
19
+ "type": "object",
20
+ "required": ["title", "description", "matching_rules"],
21
+ "properties": {
22
+ "title": {
23
+ "type": "string",
24
+ "description": "案例标题",
25
+ "minLength": 1
26
+ },
27
+ "description": {
28
+ "type": "string",
29
+ "description": "案例描述",
30
+ "minLength": 1
31
+ },
32
+ "matching_rules": {
33
+ "type": "array",
34
+ "description": "匹配规则列表",
35
+ "minItems": 1,
36
+ "items": {
37
+ "type": "object",
38
+ "required": ["field", "operator", "value"],
39
+ "properties": {
40
+ "field": {
41
+ "type": "string",
42
+ "description": "匹配字段",
43
+ "enum": ["message"]
44
+ },
45
+ "operator": {
46
+ "type": "string",
47
+ "description": "匹配操作符",
48
+ "enum": ["regex", "contains", "equals"]
49
+ },
50
+ "value": {
51
+ "type": "string",
52
+ "description": "匹配值",
53
+ "minLength": 1
54
+ }
55
+ },
56
+ "additionalProperties": false
57
+ }
58
+ },
59
+ "solution": {
60
+ "type": "string",
61
+ "description": "解决方案"
62
+ }
63
+ },
64
+ "additionalProperties": false
65
+ }
66
+ },
67
+ "additionalProperties": false
68
+ }
69
+ },
70
+ "additionalProperties": false
71
+ }
@@ -0,0 +1,58 @@
1
+ # Copyright (c) 2025 Huawei Technologies Co., Ltd.
2
+ # openUBMC is licensed under Mulan PSL v2.
3
+ # You can use this software according to the terms and conditions of the Mulan PSL v2.
4
+ # You may obtain a copy of Mulan PSL v2 at:
5
+ # http://license.coscl.org.cn/MulanPSL2
6
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
7
+ # EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
8
+ # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9
+ # See the Mulan PSL v2 for more details.
10
+ import os
11
+ import json
12
+ import stat
13
+
14
+ from conan import ConanFile
15
+ from conan.tools.files import copy, load
16
+
17
+
18
+ class BingoCasesConan(ConanFile):
19
+ name = "bingo_cases"
20
+ user = "openubmc"
21
+ channel = "stable"
22
+ settings = None
23
+ exports_sources = ["cases.yml", "cases_template_valid.json"]
24
+
25
+ def set_version(self):
26
+ import jsonschema
27
+
28
+ cases_data = self._load_cases_data()
29
+ # 验证cases模板
30
+ valid_content = load(self, "cases_template_valid.json")
31
+ valid_data = json.loads(valid_content)
32
+ jsonschema.validate(cases_data, valid_data)
33
+
34
+ self.version = cases_data["version"]
35
+
36
+ def build(self):
37
+ import yaml
38
+
39
+ cases_data = self._load_cases_data()
40
+ with os.fdopen(
41
+ os.open(
42
+ "cases.yml",
43
+ os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
44
+ stat.S_IWUSR | stat.S_IRUSR,
45
+ ),
46
+ "w",
47
+ ) as file_handler:
48
+ yaml.dump(cases_data, file_handler, encoding="utf-8", allow_unicode=True)
49
+
50
+ def package(self):
51
+ copy(self, "cases.yml", self.build_folder, self.package_folder)
52
+
53
+ def _load_cases_data(self):
54
+ import yaml
55
+
56
+ # 从 case.yml 文件读取
57
+ cases_content = load(self, "cases.yml")
58
+ return yaml.safe_load(cases_content)
bmcgo/frame.py CHANGED
@@ -45,7 +45,6 @@ class Frame(object):
45
45
  self.perf = PerfAnalysis(os.path.join(bconfig.manifest.folder, "output"))
46
46
  self.code_path = os.path.join(bconfig.manifest.folder, "build")
47
47
  sys.path.append(self.code_path)
48
- os.makedirs(misc.CACHE_DIR, exist_ok=True)
49
48
  self.ws_server = None
50
49
  self.config = config
51
50
  self.config.log_init()
@@ -217,8 +216,5 @@ class Frame(object):
217
216
 
218
217
  def _prepare(self):
219
218
  tool.sudo_passwd_check()
220
- shutil.rmtree(misc.CACHE_DIR)
221
- os.makedirs(misc.CACHE_DIR)
222
- os.chmod(misc.CACHE_DIR, 0o777)
223
219
  if shutil.which(misc.CONAN) is not None and misc.conan_v1():
224
220
  tool.run_command("conan remove --locks")
@@ -12,7 +12,6 @@
12
12
  # See the Mulan PSL v2 for more details.
13
13
  import os
14
14
  import argparse
15
- from multiprocessing import Process
16
15
  import urllib3
17
16
  from bmcgo.frame import Frame
18
17
  from bmcgo.misc import CommandInfo
@@ -60,23 +59,30 @@ class BmcgoCommand:
60
59
  self.out_dir = pre_parsed_args.out_dir
61
60
  self.lock_file = pre_parsed_args.lock_file
62
61
  self.board_name = pre_parsed_args.board_name
62
+
63
+ def component_analysis(self):
64
+ if not InterfaceValidation(self.remote).run() or not SrValidate(os.getcwd()).run():
65
+ return -1
66
+ return 0
63
67
 
68
+ def product_analysis(self, custom_sr_dir):
69
+ analysis_task = AnalysisComp(self.board_name, self.out_dir, self.lock_file, custom_sr_dir)
70
+ result = analysis_task.run()
71
+ return result
72
+
64
73
  def run(self):
65
74
  is_integrated, work_dir = self._is_integrated_project()
66
75
  if is_integrated:
67
76
  os.chdir(work_dir)
68
77
  else:
69
78
  os.chdir(self.bconfig.component.folder)
70
- # 组件级构建
79
+ # 组件级
71
80
  if not is_integrated:
72
- if not InterfaceValidation(self.remote).run() or not SrValidate(os.getcwd()).run():
73
- return -1
74
- return 0
75
- custom_sr_dir = None
76
- # 非集成环境
77
- if not is_integrated:
78
- custom_sr_dir = os.getcwd()
79
- elif self.rebuild:
81
+ return self.component_analysis()
82
+
83
+ # 产品级
84
+ custom_sr_dir = os.getcwd()
85
+ if self.rebuild:
80
86
  parsed = []
81
87
  if self.board_name:
82
88
  parsed.append("-b")
@@ -85,9 +91,9 @@ class BmcgoCommand:
85
91
  frame = Frame(self.bconfig, config)
86
92
  frame.parse(parsed)
87
93
  frame.run()
94
+
88
95
  os.chdir(os.path.join(work_dir, misc.BUILD))
89
- analysis_task = AnalysisComp(self.board_name, self.out_dir, self.lock_file, custom_sr_dir)
90
- rc = analysis_task.run()
96
+ rc = self.product_analysis(custom_sr_dir)
91
97
  if not rc:
92
98
  return -1
93
99
  log.success("BMC 构建分析成功")
@@ -34,16 +34,10 @@ def if_available(bconfig: BmcgoConfig):
34
34
  return True
35
35
 
36
36
 
37
- class BmcgoCommand(object):
37
+ class BmcgoCommand():
38
38
  def __init__(self, bconfig: BmcgoConfig, *args):
39
39
  self.bconfig = bconfig
40
- parser = argparse.ArgumentParser(prog="bmc studio", description="启动停止bmc studio", add_help=True,
41
- formatter_class=argparse.RawTextHelpFormatter)
42
- action_group = parser.add_mutually_exclusive_group()
43
- action_group.add_argument("-start", action=ACTION_TRUE, help="bmc studio的启动操作")
44
- action_group.add_argument("-stop", action=ACTION_TRUE, help="bmc studio的停止操作")
45
- action_group.add_argument("-restart", action=ACTION_TRUE, help="bmc studio的重启操作")
46
- parser.add_argument("-b", "--backend", help="指定bmc studio是前端运行还是后端运行,默认前端运行", action=ACTION_TRUE)
40
+ parser = self._create_parser()
47
41
  parsed_args, _ = parser.parse_known_args(*args)
48
42
  self.action = self.get_action(parsed_args)
49
43
  self.backend = parsed_args.backend
@@ -54,7 +48,8 @@ class BmcgoCommand(object):
54
48
  self.studio_script = f"{self.studio_path}/bmc_studio.sh"
55
49
  self.studio_command = f"{self.studio_script} {self.action}"
56
50
 
57
- def get_action(self, parsed_args):
51
+ @staticmethod
52
+ def get_action(parsed_args):
58
53
  if parsed_args.stop:
59
54
  return "stop"
60
55
  elif parsed_args.restart:
@@ -84,7 +79,7 @@ class BmcgoCommand(object):
84
79
 
85
80
  def run_start(self):
86
81
  if not self.backend:
87
- tools.run_command(self.studio_command, command_echo=False, show_log=True, timeout=None)
82
+ self._run_front_end()
88
83
  return
89
84
 
90
85
  self.studio_command = f"{self.studio_command} backend"
@@ -96,3 +91,19 @@ class BmcgoCommand(object):
96
91
  return
97
92
 
98
93
  log.warning(start_out)
94
+
95
+ def _run_front_end(self):
96
+ self.studio_command = f"trap ':' INT; /bin/bash {self.studio_command}; :"
97
+ command_list = ['/bin/bash', '-c', self.studio_command]
98
+ tools.run_command(command_list, command_echo=False, show_log=True, timeout=None)
99
+
100
+ def _create_parser(self):
101
+ _ = self
102
+ parser = argparse.ArgumentParser(prog="bmc studio", description="启动停止bmc studio", add_help=True,
103
+ formatter_class=argparse.RawTextHelpFormatter)
104
+ action_group = parser.add_mutually_exclusive_group()
105
+ action_group.add_argument("-start", action=ACTION_TRUE, help="bmc studio的启动操作")
106
+ action_group.add_argument("-stop", action=ACTION_TRUE, help="bmc studio的停止操作")
107
+ action_group.add_argument("-restart", action=ACTION_TRUE, help="bmc studio的重启操作")
108
+ parser.add_argument("-b", "--backend", help="指定bmc studio是前端运行还是后端运行,默认前端运行", action=ACTION_TRUE)
109
+ return parser