HarmonyOS开发:OpenHarmony社区——源码结构与贡献指南
HarmonyOS开发:OpenHarmony社区——源码结构与贡献指南
📌 核心要点:搞懂OpenHarmony源码结构是参与社区贡献的前提,从clone代码到提交PR,这条路没有你想的那么难走。
背景与动机
你用HarmonyOS开发应用,写了不少ArkTS代码,有一天突然想:这系统底层到底长什么样?能不能自己改一改?
然后你打开了OpenHarmony的gitee仓库,一看——好家伙,几百个仓库,几十万行代码,连README都长得像论文。你关了浏览器,心想算了算了。
这反应太正常了。OpenHarmony的源码体系确实庞大,但它不是一团乱麻,而是有清晰的分层和组织逻辑的。你之所以觉得乱,是因为还没找到那根"线头"。
这篇文章,我帮你把这根线头拽出来。从源码结构到编译构建,从社区贡献流程到SIG组织参与,一条线串起来。看完之后,你至少知道往哪个方向走,不会再在仓库首页发呆了。
核心原理
OpenHarmony源码全景
OpenHarmony不是一个大仓库,而是一个仓库集合。每个子系统有独立仓库,通过repo工具统一管理。这跟Android的AOSP用repo管理多仓库是一个思路。
graph TB
A[OpenHarmony 源码体系] --> B[内核层 kernel]
A --> C[系统服务层 system]
A --> D[框架层 arkui]
A --> E[应用层 apps]
A --> F[编译构建 build]
A --> G[测试 testing]
A --> H[驱动框架 drivers]
B --> B1[linux_kernel]
B --> B2[liteos_m]
B --> B3[liteos_a]
C --> C1[abilitymgr]
C --> C2[bundlemgr]
C --> C3[accountmgr]
C --> C4[communication]
D --> D1[arkui_ace_engine]
D --> D2[arkcompiler]
D --> D3[ability_runtime]
E --> E1[launcher]
E --> E2[settings]
E --> E3[camera]
classDef root fill:#E91E63,stroke:#880E4F,color:#fff
classDef layer fill:#2196F3,stroke:#1565C0,color:#fff
classDef component fill:#4CAF50,stroke:#2E7D32,color:#fff
class A root
class B,C,D,E,F,G,H layer
class B1,B2,B3,C1,C2,C3,C4,D1,D2,D3,E1,E2,E3 component
四层架构与仓库映射
OpenHarmony的架构分四层,每层对应一组仓库:
| 层级 | 职责 | 核心仓库 | 你会接触的频率 |
|---|---|---|---|
| 内核层 | 操作系统内核 | kernel_linux_5.10, kernel_liteos_a |
低(除非做驱动) |
| 系统服务层 | 系统基础服务 | abilitymgr_runtime, bundle_framework |
中(改系统行为时) |
| 框架层 | UI框架与编译器 | arkui_ace_engine, arkcompiler_toolchain |
高(写应用经常查源码) |
| 应用层 | 系统应用 | apps_launcher, apps_settings |
高(参考系统应用实现) |
关键目录结构
当你用repo拉下完整源码后,根目录长这样:
openharmony/
├── applications/ # 系统应用(Launcher、Settings等)
├── ark/ # ArkCompiler编译器
├── base/ # 系统基础服务
│ ├── account/ # 账号管理
│ ├── communication/ # 通信服务(蓝牙、WiFi等)
│ ├── location/ # 定位服务
│ └── ...
├── build/ # 编译构建脚本
├── drivers/ # 驱动框架
├── foundation/ # 核心基础能力
│ ├── arkui/ # ArkUI引擎
│ ├── ability/ # Ability运行时
│ └── ...
├── kernel/ # 内核
├── out/ # 编译输出
├── test/ # 测试框架
├── third_party/ # 三方库
└── vendor/ # 厂商配置
你不用记住每个目录,但你需要知道:改UI框架去foundation/arkui,改系统服务去base/,改系统应用去applications/。
编译构建体系
OpenHarmony用GN + Ninja做构建。GN负责生成构建配置,Ninja负责实际编译。整个流程:
graph LR
A[repo sync 拉取源码] --> B[./build.sh --product-name <产品名>]
B --> C[GN生成Ninja文件]
C --> D[Ninja编译]
D --> E[输出镜像到out/目录]
E --> F[烧录到设备]
classDef step fill:#FF9800,stroke:#E65100,color:#fff
classDef output fill:#4CAF50,stroke:#2E7D32,color:#fff
class A,B,C,D step
class E,F output
编译命令很简单,但选对产品名是关键:
# 编译标准系统(手机/平板)
./build.sh --product-name rk3568
# 编译小型系统(IoT设备)
./build.sh --product-name hi3861
# 编译迷你系统(穿戴设备)
./build.sh --product-name hi3516dv300
SIG组织架构
SIG(Special Interest Group)是OpenHarmony社区的组织形式。每个SIG负责一个技术方向,有独立的维护者和贡献者。
| SIG名称 | 负责方向 | 活跃度 |
|---|---|---|
| sig-arkui | ArkUI框架 | ⭐⭐⭐⭐⭐ |
| sig-compiler | 编译器与运行时 | ⭐⭐⭐⭐ |
| sig-application | 应用框架 | ⭐⭐⭐⭐ |
| sig-distributedhw | 分布式硬件 | ⭐⭐⭐ |
| sig-security | 安全 | ⭐⭐⭐ |
| sig-drivers | 驱动框架 | ⭐⭐ |
代码实战
基础用法:拉取源码与首次编译
第一步,你得把代码拉下来。别用git clone一个个拉,OpenHarmony有几百个仓库,你clone到猴年马月?用repo工具。
// 这不是ArkTS代码,是shell脚本,用于拉取和编译OpenHarmony源码
// 1. 安装repo工具
// curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > ~/bin/repo
// chmod a+x ~/bin/repo
// 2. 初始化仓库(以master分支为例)
// mkdir openharmony && cd openharmony
// repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify
// 3. 拉取源码
// repo sync -c
// 4. 首次编译(RK3568开发板)
// ./build.sh --product-name rk3568 -ccache
// 5. 查看编译输出
// ls out/rk3568/packages/phone/images/
首次编译时间很长,快的也要一两个小时,慢的四五个小时都有。别急,去喝杯咖啡。
进阶用法:贡献代码的完整流程
光拉代码不够,你得会提交贡献。OpenHarmony的贡献流程遵循标准的开源社区模式:Fork → 修改 → PR → Review → Merge。
// 贡献流程示例:修复ArkUI的一个Bug
//
// 步骤1:在Gitee上Fork目标仓库
// 比如你要修改arkui_ace_engine,先Fork到自己的仓库
//
// 步骤2:添加你的Fork为远程仓库
// git remote add myfork https://gitee.com/你的用户名/arkui_ace_engine.git
//
// 步骤3:创建特性分支
// git checkout -b fix/issue-12345-text-overflow
//
// 步骤4:修改代码并提交
// git add .
// git commit -s -m "fix: 修复Text组件overflow属性在特定场景下失效的问题"
// 注意:-s 参数会自动添加 Signed-off-by 行,这是DCO要求
//
// 步骤5:推送到你的Fork
// git push myfork fix/issue-12345-text-overflow
//
// 步骤6:在Gitee上创建Pull Request
// 目标:openharmony/arkui_ace_engine 的 master 分支
// 来源:你的 fix/issue-12345-text-overflow 分支
//
// 步骤7:等待Review
// 维护者会审查你的代码,可能提出修改意见
// 根据意见修改后,追加提交即可
完整示例:自动化贡献检查工具
手动走贡献流程容易出错——忘了sign-off、commit message格式不对、没跑测试……写个脚本把这些检查自动化。
// ContributionChecker.ets - 贡献前自动检查工具
import { exec } from '@ohos.child_process';
interface CheckResult {
passed: boolean;
message: string;
}
export class ContributionChecker {
private projectPath: string;
constructor(projectPath: string) {
this.projectPath = projectPath;
}
// 执行所有检查
async runAllChecks(): Promise<CheckResult[]> {
const results: CheckResult[] = [];
results.push(await this.checkGitStatus());
results.push(await this.checkCommitMessage());
results.push(await this.checkSignedOff());
results.push(await this.checkLicenseHeader());
results.push(await this.checkBuildPass());
return results;
}
// 检查1:确认工作区干净
async checkGitStatus(): Promise<CheckResult> {
try {
const result = await this.execCommand('git status --porcelain');
if (result.trim().length > 0) {
return {
passed: false,
message: '❌ 工作区有未提交的修改,请先提交或暂存'
};
}
return { passed: true, message: '✅ 工作区干净' };
} catch (err) {
return { passed: false, message: `❌ Git状态检查失败: ${err}` };
}
}
// 检查2:commit message格式
async checkCommitMessage(): Promise<CheckResult> {
try {
const result = await this.execCommand('git log -1 --format=%s');
const subject = result.trim();
// OpenHarmony要求的格式:type: 描述
const validPrefixes = ['feat:', 'fix:', 'docs:', 'style:', 'refactor:',
'test:', 'chore:', 'revert:'];
const hasValidPrefix = validPrefixes.some(prefix => subject.startsWith(prefix));
if (!hasValidPrefix) {
return {
passed: false,
message: `❌ Commit message格式不对: "${subject}"\n 应以 feat:/fix:/docs: 等前缀开头`
};
}
if (subject.length > 72) {
return {
passed: false,
message: `❌ Commit message太长(${subject.length}字符),建议不超过72字符`
};
}
return { passed: true, message: `✅ Commit message格式正确: "${subject}"` };
} catch (err) {
return { passed: false, message: `❌ Commit message检查失败: ${err}` };
}
}
// 检查3:Signed-off-by(DCO要求)
async checkSignedOff(): Promise<CheckResult> {
try {
const result = await this.execCommand('git log -1 --format=%B');
const body = result.trim();
if (!body.includes('Signed-off-by:')) {
return {
passed: false,
message: '❌ 缺少Signed-off-by行,提交时请使用 -s 参数'
};
}
return { passed: true, message: '✅ Signed-off-by检查通过' };
} catch (err) {
return { passed: false, message: `❌ Signed-off-by检查失败: ${err}` };
}
}
// 检查4:文件头License声明
async checkLicenseHeader(): Promise<CheckResult> {
try {
// 查找修改的文件列表
const diffResult = await this.execCommand('git diff --name-only HEAD~1');
const files = diffResult.trim().split('\n').filter(f => f.length > 0);
const ohosLicense = 'Copyright (c) 2024 Huawei Device Co., Ltd.';
let missingFiles: string[] = [];
for (const file of files) {
// 只检查.ts/.ets/.cpp/.h文件
if (/\.(ts|ets|cpp|h)$/.test(file)) {
const catResult = await this.execCommand(`head -5 ${file}`);
if (!catResult.includes(ohosLicense) && !catResult.includes('Copyright')) {
missingFiles.push(file);
}
}
}
if (missingFiles.length > 0) {
return {
passed: false,
message: `❌ 以下文件缺少License头:\n ${missingFiles.join('\n ')}`
};
}
return { passed: true, message: '✅ License头检查通过' };
} catch (err) {
return { passed: false, message: `❌ License检查失败: ${err}` };
}
}
// 检查5:编译是否通过
async checkBuildPass(): Promise<CheckResult> {
try {
// 只做增量编译检查
const result = await this.execCommand(
'./build.sh --product-name rk3568 --build-target arkui_ace_engine 2>&1 | tail -5'
);
if (result.includes('FAILED')) {
return { passed: false, message: '❌ 编译未通过,请修复编译错误' };
}
return { passed: true, message: '✅ 编译检查通过' };
} catch (err) {
return {
passed: true,
message: '⚠️ 跳过编译检查(可能不在源码根目录)'
};
}
}
// 执行命令的辅助方法
private execCommand(cmd: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(cmd, { cwd: this.projectPath }, (err, stdout) => {
if (err) reject(err);
else resolve(stdout);
});
});
}
}
// 使用示例
async function main() {
const checker = new ContributionChecker('/path/to/openharmony');
const results = await checker.runAllChecks();
console.log('===== 贡献检查报告 =====');
let allPassed = true;
for (const result of results) {
console.log(result.message);
if (!result.passed) allPassed = false;
}
if (allPassed) {
console.log('\n🎉 所有检查通过,可以提交PR了!');
} else {
console.log('\n⚠️ 存在未通过的检查,请修复后再提交');
}
}
踩坑与注意事项
坑1:repo sync失败
这是新手最常遇到的问题。几百个仓库,总有一个会超时。
解法:加-j1参数,单线程拉取。慢是慢了点,但稳定:
repo sync -c -j1
如果某个仓库反复失败,单独拉它:
repo sync -c kernel_linux_5.10
坑2:编译依赖缺失
首次编译报错"xxx not found",大概率是依赖没装全。OpenHarmony的编译依赖列表很长,官方文档有,但容易漏。
关键依赖(Ubuntu环境):
sudo apt-get install gcc g++ make zlib1g-dev libffi-dev \
libssl-dev python3 python3-pip flex bison
pip3 install pyyaml
坑3:PR被拒——DCO检查失败
OpenHarmony社区要求所有提交都带Signed-off-by行,这是DCO(Developer Certificate of Origin)的要求。你忘了加这个,PR直接被CI拦截。
解法:提交时加-s参数:
git commit -s -m "fix: 修复xxx问题"
坑4:commit message格式不对
OpenHarmony对commit message有严格要求,格式必须是:
<type>: <subject>
<body>
< trailers >
type只能是这几个:feat、fix、docs、style、refactor、test、chore、revert。你写个"update"或"修改bug",直接打回。
坑5:找不到对应的SIG
你想贡献代码,但不知道该找哪个SIG。这很常见,因为有些仓库的归属不太明显。
解法:看仓库的OWNERS文件。每个仓库根目录都有这个文件,里面列出了维护者。你给维护者发邮件或在Gitee上@他,比漫无目的地在社区里问效率高得多。
坑6:编译时间过长
全量编译动辄几小时,改一行代码等几小时,谁受得了?
解法:用增量编译。只编译你修改的模块:
# 只编译ArkUI引擎
./build.sh --product-name rk3568 --build-target arkui_ace_engine
# 只编译某个测试
./build.sh --product-name rk3568 --build-target ace_engine_unittest
HarmonyOS 6适配说明
HarmonyOS 6基于OpenHarmony 5.x,有几个重要变化:
-
构建系统升级:从GN逐步迁移到hvigor构建体系,部分模块的构建配置文件格式变了。如果你之前改过
BUILD.gn,需要检查是否需要适配hvigorfile.ts。 -
ArkUI引擎重构:ACE引擎的部分C++接口做了调整,如果你贡献过ArkUI底层代码,需要关注API变更。
-
CI流程更新:社区CI新增了静态分析步骤,对代码规范的要求更严格了。以前能过的PR,现在可能被拦截。
-
新增SIG:HarmonyOS 6引入了AI能力框架,对应新增了
sig-ai_framework。如果你对端侧AI感兴趣,这是个好的切入点。
总结
OpenHarmony的源码体系和贡献流程,看着吓人,其实就那几步:拉代码、找模块、改代码、提PR。难的不是技术,是你第一次打开那个几百个仓库的页面时,不知道从哪下手。
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐ 源码量大,需要耐心摸索 |
| 使用频率 | ⭐⭐⭐ 做系统级开发时经常需要 |
| 重要程度 | ⭐⭐⭐⭐⭐ 深入理解系统的必经之路 |
几个关键建议:
- 从应用层入手:别一上来就啃内核,先从
applications/目录的系统应用开始,这些代码量小、逻辑清晰,容易上手。 - 先修Bug再提Feature:社区更信任修过Bug的人。你在Issue列表里找一个简单Bug修了,比直接提一个大Feature更容易被接受。
- 多看别人的PR:看别人怎么写的commit message、怎么组织代码的、Review意见是什么。这是最快的学习方式。
- 加入SIG的例会:大部分SIG都有定期线上会议,参加一次比看十篇文档管用。
- 点赞
- 收藏
- 关注作者
评论(0)