R010: partname/subsystemname不匹配
SkillDev toolsA skill for dev tools by openharmonyinsight.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the R010: partname/subsystemname不匹配 skill
What this skill tells your AI
The instructions your AI receives, as published by openharmonyinsight/openharmony-skills in skills/check-test-code-quality/rules/R010/SKILL.md and read by ahel’s review.
规则信息
| 属性 | 值 |
|---|---|
| 规则编号 | R010 |
| 问题类型 | part_name/subsystem_name不匹配 |
| 严重级别 | Critical |
| 规则复杂度 | complex |
| 扫描范围 | BUILD.gn files only |
| testcase字段 | -(BUILD.gn为非测试文件,无对应it()块) |
问题描述
part_name/subsystem_name不匹配。BUILD.gn中声明的part_name必须在对应subsystem_name的components列表中存在。
修复建议
使用完整的子系统-组件映射表,确保part_name在对应subsystem的components中。
映射表数据源
映射表从以下三个配置文件构建(合并所有 subsystem → component 关系,共54个子系统、332个部件):
| 配置文件 | 主URL(推荐) | 备用URL |
|---|---|---|
| vendor_hihope config.json | https://gitee.com/openharmony/vendor_hihope/raw/master/rk3568/config.json | https://gitcode.com/openharmony/vendor_hihope/raw/master/rk3568/config.json |
| productdefine rich.json | https://gitee.com/openharmony/productdefine_common/raw/master/inherit/rich.json | https://gitcode.com/openharmony/productdefine_common/raw/master/inherit/rich.json |
| productdefine chipset_common.json | https://gitee.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json | https://gitcode.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json |
重要: 优先使用gitee.com URL,gitcode.com可能需要认证。如果所有URL均不可达,必须在终端输出明确警告,不可静默返回0个问题。
本地缓存: 获取成功后可将映射表缓存到本地文件(如/tmp/r010_mapping.json),下次扫描优先读取缓存,避免每次都请求远程。
扫描逻辑
Step 1: 收集文件
扫描目标目录下所有名为BUILD.gn的文件。
import os
def find_build_gn_files(scan_root):
build_gn_files = []
for dirpath, dirnames, filenames in os.walk(scan_root):
if 'BUILD.gn' in filenames:
build_gn_files.append(os.path.join(dirpath, 'BUILD.gn'))
return build_gn_files
Step 2: 解析BUILD.gn提取part_name和subsystem_name
对每个BUILD.gn文件,使用正则表达式提取part_name和subsystem_name字段。
import re
def extract_build_gn_fields(content):
part_name = None
subsystem_name = None
part_match = re.search(r'part_name\s*=\s*["\']([^"\']+)["\']', content)
if part_match:
part_name = part_match.group(1)
subsystem_match = re.search(r'subsystem_name\s*=\s*["\']([^"\']+)["\']', content)
if subsystem_match:
subsystem_name = subsystem_match.group(1)
return part_name, subsystem_name
Step 3: 构建子系统-组件映射表
从三个配置文件中读取并合并映射关系。每个配置文件的结构包含子系统定义,每个子系统下有components列表。
⚠️ 数据格式陷阱: components数组中的元素不是纯字符串,而是对象{"component": "xxx", "features": [...]}。直接用set().update(components)会抛unhashable type: 'dict'异常,导致映射表构建失败、R010静默返回0个问题。必须从每个对象中提取component字段。
import json
MAPPING_URLS = [
'https://gitee.com/openharmony/vendor_hihope/raw/master/rk3568/config.json',
'https://gitee.com/openharmony/productdefine_common/raw/master/inherit/rich.json',
'https://gitee.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json',
]
CACHE_FILE = '/tmp/r010_mapping.json'
def _parse_config(data, subsystem_map):
subsystems = data.get('subsystems', [])
for subsys in subsystems:
name = subsys.get('subsystem', '')
components = subsys.get('components', [])
if name not in subsystem_map:
subsystem_map[name] = set()
for c in components:
if isinstance(c, str):
subsystem_map[name].add(c)
elif isinstance(c, dict):
subsystem_map[name].add(c.get('component', ''))
def load_subsystem_mapping():
# 优先读取本地缓存
import os
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r', encoding='utf-8') as f:
return {k: set(v) for k, v in json.load(f).items()}
# 从远程获取
import urllib.request
subsystem_map = {}
for url in MAPPING_URLS:
try:
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read().decode('utf-8'))
_parse_config(data, subsystem_map)
except Exception as e:
print(f" 警告: 获取映射表失败 {url}: {e}", file=sys.stderr, flush=True)
if not subsystem_map:
print(" 警告: R010映射表为空,所有BUILD.gn将被跳过!", file=sys.stderr, flush=True)
# 写入缓存
with open(CACHE_FILE, 'w', encoding='utf-8') as f:
json.dump({k: sorted(v) for k, v in subsystem_map.items()}, f, ensure_ascii=False)
return subsystem_map
Step 4: 验证匹配关系
对每个BUILD.gn,检查提取出的part_name是否在subsystem_name对应的components集合中。
def validate_part_subsystem(part_name, subsystem_name, subsystem_map):
if not part_name or not subsystem_name:
return True
if subsystem_name not in subsystem_map:
return True
return part_name in subsystem_map[subsystem_name]
Step 5: 生成问题报告
def scan_r010(build_gn_files, base_dir):
issues = []
subsystem_map = load_subsystem_mapping()
if not subsystem_map:
return issues
for file_path in build_gn_files:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
part_name, subsystem_name = extract_build_gn_fields(content)
if not part_name or not subsystem_name:
continue
if not validate_part_subsystem(part_name, subsystem_name, subsystem_map):
rel_path = os.path.relpath(file_path, base_dir)
part_line = 0
for i, line in enumerate(content.splitlines(), 1):
if re.search(r'part_name\s*=\s*["\']' + re.escape(part_name) + '["\']', line):
part_line = i
break
available_parts = sorted(subsystem_map.get(subsystem_name, set()))
issues.append({
'rule': 'R010',
'type': 'part_name/subsystem_name不匹配',
'severity': 'Critical',
'file': rel_path,
'line': part_line,
'testcase': '-',
'snippet': f'part_name = "{part_name}", subsystem_name = "{subsystem_name}"',
'suggestion': (
f'part_name "{part_name}" 不在 subsystem_name "{subsystem_name}" 的components中。'
f'可选的part_name: {available_parts}'
),
})
return issues
错误示例
# BUILD.gn - 错误:invalid_part不在arkui的components中
ohos_js_app_suite("ActsTest") {
testonly = true
part_name = "invalid_part" # ✗ 错误
subsystem_name = "arkui"
hap_name = "ActsTest"
}
正确示例
# BUILD.gn - 正确:ace_engine在arkui的components中
ohos_js_app_suite("test") {
testonly = true
part_name = "ace_engine" # ✓ 正确
subsystem_name = "arkui"
hap_name = "ActsTest"
}
注意事项
- 如果
subsystem_name本身不在映射表中,跳过验证(可能是新增子系统) - 映射表需从三个配置文件合并,任何一个文件中出现的映射关系都有效
- BUILD.gn文件中可能存在多个target,每个target都需要检查
- 不检查group类型的BUILD.gn(group类型通常不包含part_name/subsystem_name)
- 如果BUILD.gn文件中缺少part_name或subsystem_name字段,跳过该文件
- 映射表获取失败时必须告警: 如果远程URL不可达且本地无缓存,必须在终端输出明确警告,不可静默返回空结果
陷阱与注意事项
陷阱1: components是对象数组而非字符串数组
严重性: 严重,导致unhashable type: 'dict'异常,映射表构建失败。
三个配置文件中components字段的实际数据格式为对象数组,不是纯字符串数组:
{
"subsystem": "arkui",
"components": [
{"component": "ace_engine", "features": []},
{"component": "napi", "features": []}
]
}
错误做法: 直接set().update(components) → 抛异常
正确做法: 逐个判断类型,对象取component字段:
for c in components:
if isinstance(c, str):
mapping[name].add(c)
elif isinstance(c, dict):
mapping[name].add(c.get('component', ''))
陷阱2: gitcode.com URL需要认证
gitcode.com的raw文件URL需要认证才能访问,直接请求会返回HTML登录页而非JSON。必须优先使用gitee.com URL。
陷阱3: 映射表为空时静默返回0
如果映射表获取失败(网络不通、认证失败等),映射表为空,所有subsystem_name都不在映射表中,全部被跳过,R010返回0个问题且不报任何错误。必须在映射表为空时输出明确警告。
输出格式
每条issue的字段:
| 字段 | 值 |
|---|---|
| rule | R010 |
| type | part_name/subsystem_name不匹配 |
| severity | Critical |
| file | 相对路径(如ability/xxx/BUILD.gn) |
| line | part_name所在行号 |
| testcase | - |
| snippet | part_name = "xxx", subsystem_name = "yyy" |
| suggestion | 问题描述 + 可选的part_name列表 |
技术挑战与解决方案
维护完整的子系统-组件映射表
挑战: 维护完整的子系统-组件映射表
解决方案:
SUBSYSTEM_PARTS_MAP = {
"account": ["os_account"],
"arkui": ["ace_engine", "napi", "ui_appearance", "ui_lite"],
"bundle": ["bundle_framework"],
"communication": ["bluetooth", "wifi", "netstack", "dsoftbus"],
# ... 共50+个子系统
}
Signals
- GitHub stars
- 34
- Forks
- 7
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
r010- Source
- github.com/openharmonyinsight/openharmony-skills