☰
鸿蒙系统源码下载指南:repo多仓库同步与编译环境配置
2026/11/3 8:53:45 网站建设 项目流程

前几天有个做嵌入式的朋友问我:鸿蒙系统源码下载到底该怎么弄,是不是去某个页面填个表就能拿到一个大压缩包?我当时就笑了——这个误解太普遍了。鸿蒙系统的源码不是一个压缩包,而是一棵由几十个甚至上百个 Git 仓库拼起来的大树,靠一份 manifest 清单文件把你需要的那几百个仓库在某个时间点的提交号钉死,再用 repo 这个工具统一拉下来。你要做的不是“下载”,而是“同步一套版本快照”。

这篇东西写给三类人:想读内核和框架源码但被仓库结构劝退的开发者、想在自己板子上跑通一次完整编译的嵌入式玩家、以及需要评估移植或二次开发成本的团队技术负责人。我会把“鸿蒙系统源码下载”这件事从头到尾拆开讲:源码分成哪几层、环境怎么盘、命令怎么敲、参数为什么这么设、坑在哪里。命令我会给可复制的形态,但仓库地址一律用占位符,因为具体地址以官方文档公布的为准,而且版本迭代快,写死了反而害你。

1. 先搞清楚“鸿蒙源码”到底有几层,别一上来就找压缩包

我见过太多人卡在第一步,不是因为技术难,而是因为目标没定义清楚。说“我要鸿蒙源码”,可能指的是四种完全不同的东西,对应四条完全不同的获取路径。搞混了,你会发现下了几十个 G,打开一看却没有你想要的那部分代码。

1.1 社区开源版、商用发行版、设备侧工具包、三方移植仓

第一层是社区开源版。这是真正意义上公开、可自由下载的那部分,社区里通常叫 OpenHarmony。它包含内核适配层、系统服务框架、应用框架、图形、多媒体、编译构建系统等,是最适合学习和研究的对象。你听到的“鸿蒙系统源码下载”,九成指的是这一层。

第二层是商用发行版。厂商基于开源版做裁剪、加固、增加自有服务和驱动适配,形成可以装到量产设备上的系统。这一层的完整源码一般不会整体公开,对外提供的是 SDK、HDK、开发文档和部分头文件。如果你是做产品集成,拿的是这一层的东西,别指望能 clone 到全部代码。

第三层是设备侧工具包与预编译产物。包括交叉编译工具链、系统镜像、签名工具、烧录工具。它们不是源码,但很多新手会把“下载了一个 SDK 包”当成“下载了源码”,然后在里面翻 .c 文件翻不到,白折腾半天。

第四层是芯片与板卡厂商的移植仓。这些仓库通常挂在开源版组织之外,或者在 vendor、device 目录下以独立仓库的形式存在。你要在特定开发板上跑,就离不开这一层,而这一层往往是文档最少、坑最多的地方。

我的建议是:先明确你要的是“读代码”还是“跑编译”。只读代码,第一层足够;要跑编译,第一层加第三层;要上板,四层全要。

1.2 为什么官方更愿意让你用 repo,而不是给你一个 zip

很多人第一反应是:给我一个 zip 多省事。但从项目方的角度看,一个大 zip 是灾难。原因有三个,都很实在。

第一,解耦与按需。这套系统由几百个仓库组成,图形、媒体、通信、内核适配各管各的,各有各的维护者和提交节奏。做成一个大仓库,任何人改一行代码都要跟所有人的提交打架,评审和合入会直接瘫痪。拆成多仓库之后,某个子系统单独发版、单独回退都很轻松。

第二,可复现。manifest 文件里记录的是“每个仓库对应哪个 commit”,相当于一张精确的配料表。今天拉下来的代码和三个月后拉下来的代码,只要 manifest 一致,编译出来的东西就是一致的。这对定位问题太重要了——线上出现一个诡异 bug,你能精确回到当时那个代码状态。

第三,传输效率。一个大 zip 意味着每次更新都要重新下一遍全量。而 Git 只传增量对象,你昨天同步过,今天再同步可能只下几十兆。

打个比方:大 zip 像是把整栋楼的所有房间钥匙熔成一把万能钥匙,丢了就得重铸;repo 加 manifest 像是给你一串编号清晰的钥匙,哪把丢了配哪把,还能记录“今天这串钥匙开哪几扇门”。

提示:不要用“下载一个 release 压缩包然后改代码”的方式做开发。没有 .git 目录,你既没法看提交历史,也没法做 diff 和回退,出了问题只能靠肉眼比对。

2. 下载前的环境盘点:磁盘、内存、网络三笔账

真正开始敲命令之前,先把账算清楚。我见过最惨的一次,是某位同学在 128G 的笔记本上跑到一半磁盘满了,repo 报了一堆莫名其妙的错误,他以为是网络问题,折腾了一整天。

2.1 磁盘空间到底要留多少,我给你算笔明细

先说结论:准备 200GB 空闲空间,用 ext4 或 xfs,别用 NTFS 或 exFAT。下面是拆解。

源码树本身,如果只拉当前分支(-c参数),工作区文件大概十几到二十几 GB;加上每个仓库的 .git 对象库,总量会到三十 GB 上下。如果拉全量历史,翻倍很正常。

编译产物 out 目录,取决于你编译的范围。全量编译一个中等规模的产品配置,产物加中间文件二十到四十 GB 是常态。你要是同时编多个产品配置,out 目录会成倍增长。

ccache 缓存,如果你开了编译缓存(强烈建议开),第一次编译完可能占十几 GB,之后增长会放缓。

再加上工具链、预编译二进制、日志,零零碎碎又是几个 GB。所以:

使用场景源码编译产物缓存建议预留
只读代码、不编译15-30 GB0050 GB
单产品配置编译20-35 GB20-40 GB10-20 GB120 GB
多产品配置 + 反复编译30-50 GB40-80 GB20-40 GB250 GB

还有一个容易被忽略的点:inode。这套源码小文件极多,几十万个文件很正常。有些云主机默认 inode 数量少,磁盘还有空间但df -i已经 100%,写入会直接失败。所以我习惯在同步前先跑一遍df -h和df -i双检查。

内存方面,8GB 能跑起来但会很憋屈,16GB 是比较舒服的起点,32GB 在跑并行编译时优势明显。如果内存不够又想编译,就把并行度降下来,别硬顶,OOM Killer 会在你编译到 80% 的时候毫不留情地干掉进程。

2.2 系统与工具链依赖清单

依赖项版本建议作用踩坑提示
Linux 发行版Ubuntu 20.04/22.04 等主流 LTS编译宿主环境内核版本别太老
git2.20 以上版本控制太老不支持 partial clone
python33.8-3.10repo 脚本运行环境版本过高可能报语法错
repo最新版脚本多仓库管理用包管理器装的常偏旧
gn / ninja随源码预置或自装构建系统注意 PATH 顺序
clang源码内预置编译器别用系统自带版本覆盖
nodejs14-18部分构建脚本依赖版本漂移易报错
ccache任意较新版编译加速单独挂盘更省心

为什么必须在 Linux 上做这件事?不是矫情,是硬约束。第一,大小写敏感。这套代码里存在路径只差大小写的文件,在不区分大小写的文件系统上会直接互相覆盖,编译必然失败。第二,符号链接。构建过程大量依赖软链接,Windows 文件系统对软链接的支持很别扭。第三,文件权限。一堆构建脚本需要可执行位,权限位丢失就跑不起来。第四,路径长度。深层嵌套目录很容易突破 Windows 的路径长度限制。

那 Windows 用户怎么办?三条路:WSL2、虚拟机、双系统。WSL2 最省事,但要注意把代码放在 WSL 自己的文件系统里(比如/home/xxx/),千万别放在/mnt/c/下——跨文件系统访问会让同步和编译慢到怀疑人生,我实测过,差距能有五到十倍。虚拟机隔离性好但吃资源。双系统性能最好但切换麻烦。

2.3 网络不顺畅时的四条替代路径

同步卡住是常态,尤其仓库多、单个仓库大的时候。我不谈任何绕过网络限制的手段,只说正当的技术优化,这些手段本身就够用了。

路径一:用就近的代码托管镜像。同一份代码在多个平台都有官方同步的镜像仓库,选物理距离近、响应快的那个,manifest 里的 remote 地址可以替换。这一步能解决大部分“慢”的问题。

路径二:浅克隆。--depth=1只拉最新一次提交,不下历史。对只读代码和跑编译的场景完全够用,体积能砍掉一大半。代价是看不到历史提交,也不能直接切老版本。

路径三:单仓库按需拉取。不跑全量repo sync,先repo sync指定几个你关心的路径,把最核心的拉下来,其余的以后再说。这个策略在磁盘紧张或者只想看某个子系统时特别有效。

路径四:用官方归档包。有些版本会提供打包好的源码归档,适合做离线存档、教学演示、代码审计。缺点是更新不及时,且没有版本控制信息。

注意:无论用哪条路径,都不要在同步过程中手动去删 .repo 目录下的文件。那个目录是 repo 的状态机,删了之后它会认为从没同步过,你得从头再来。

3. 实操:从零到源码落地的完整流程

前面铺垫这么多,是因为我发现跳过环境检查直接敲命令的人,八成会在中途返工。下面是我自己用的一套流程,按顺序走基本不会翻车。

3.1 装 repo 的两种方式,我推荐手动放脚本

方式一:包管理器安装。一行命令搞定,省事。问题是发行版仓库里的版本经常滞后,遇到 manifest 用了新特性就可能报错,而且报错信息往往很隐晦,让人误以为是仓库地址错了。

方式二:手动下载脚本放到 PATH 里。这是我推荐的做法,可控性强。

mkdir -p ~/bin curl -o ~/bin/repo https://<代码托管平台>/<组织>/repo/raw/<分支>/repo chmod a+x ~/bin/repo export PATH=$HOME/bin:$PATH echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc

几个细节值得说。~/bin是约定俗成的用户级可执行目录,不需要 sudo,避免污染系统环境。chmod a+x千万别忘,忘了他会报“permission denied”,很多人第一反应是去加 sudo,越搞越乱。写完 PATH 记得source ~/.bashrc,并且新开一个终端验证一下which repo指向的是不是你刚放的那个。

装完立刻验证:

repo --version python3 --version

两行都出结果,才算环境就绪。如果repo --version报语法错,多半是 python 版本不匹配,用update-alternatives或者虚拟环境切一下。

3.2 初始化:分支和标签选错,后面全白干

初始化的核心是搞清楚-b和-m这两个参数到底在指定什么。

mkdir -p ~/oh_workspace cd ~/oh_workspace repo init -u https://<代码托管平台>/<组织>/manifest.git -b <manifest分支名> -m default.xml

-u指向的是manifest 仓库,注意,不是代码仓库,这是个极高频的错误。很多人把某个子系统的仓库地址填进去,repo 会告诉你找不到 manifest 文件。

-b指定的是manifest 仓库的分支,不是代码的分支。这一点极其反直觉。你选-b的那个分支,决定了 manifest 文件里记录的是哪一套仓库组合和提交号。

-m指定用哪个 xml 文件。默认是default.xml,有些版本会提供default.xml之外的组合文件,比如精简版、带特定硬件适配的版本。

关于选主干还是选发布标签,我的经验是:学习和研究用发布标签,跟进新特性用主干,做产品基线必须锁标签。理由很直接——主干每天都在变,今天能编过的代码明天可能就编不过;而标签和发布版本是一一对应的,可复现,出了问题能查。

初始化成功后,目录下会多出一个.repo目录。这时候别急着 sync,先看一眼清单:

ls .repo/manifests/ cat .repo/manifest.xml | head -50

你会看到一堆<project>标签,每个对应一个仓库和它的路径、revision。这就是你的“配料表”,后面所有同步行为都由它决定。

3.3 同步:并发数不是越大越好,断点续传是免费的

repo sync -c --no-tags --force-sync -j8

逐个参数解释。-c表示只拉当前分支,能省下大量历史对象,是最划算的一个优化。--no-tags不拉标签,进一步减体积。--force-sync在本地有改动冲突时强制覆盖——这个参数要谨慎,它会覆盖你在工作区的未提交修改,只在确认本地无改动时用。-j8是并发数,同时拉 8 个仓库。

关于-j,我踩过坑。设成 32,结果网络跑满了但同步速度反而更慢,因为每个连接分到的带宽太薄,加上服务端可能有并发限制,大量请求排队超时,repo 不断重试,看起来就是“卡住不动”。后来我固定用 4 到 8,视带宽和 CPU 而定,反而最快。经验值:百兆带宽用 -j4,千兆带宽用 -j8,别超过 CPU 核数太多。

同步中断了怎么办?直接重新执行同一条命令即可。repo 会检查每个仓库的本地状态,已经完整的跳过,没完成的续上,这是它比手工 clone 强的地方。所以千万别因为断了就rm -rf重来,那是纯浪费时间。

如果全量同步太慢,用分批策略:

repo sync -c build repo sync -c foundation/ability repo sync -c kernel

先把你最关心的几个路径拉下来,能打开 IDE 看代码了,剩下的慢慢补。这个策略对只想读某个子系统源码的人特别友好,可能一两个小时就能开始干活。

3.4 校验完整性:花五分钟省一天

同步跑完,很多人直接就开编了。我建议加一步校验,五分钟的事,能省掉后面一天的排查。

repo status repo forall -c 'git status --porcelain' | head -50 repo manifest -r -o snapshot_$(date +%Y%m%d).xml

第一条看整体状态。第二条逐个仓库检查工作区是否干净——如果同步完就有未跟踪文件或修改,那多半是同步过程中断了,某个仓库没拉全。第三条最重要:-r会把当前每个仓库实际所在的 commit 号固化进一个新的 xml 文件。

这个快照文件的价值在于:三个月后你想回到今天这个状态,只要用这个 xml 重新 init 再 sync,就能一字不差地复现。做产品的团队一定要把这个文件纳入版本管理,它是你构建基线的凭证。

再补一步,检查有没有仓库处于异常状态:

repo forall -c 'git rev-parse --verify HEAD > /dev/null || echo "BROKEN: $REPO_PATH"'

输出为空说明全部正常。有 BROKEN 的,单独对该路径执行一次 sync 就行。

4. 只要某个模块?按需拉取的三种姿势

不是所有人都需要整棵树。我经常遇到这种情况:只想看看某个子系统的实现,或者只想给某个仓库提个补丁,结果为了这一个仓库要同步几十 G,纯属浪费。下面三种姿势按需选。

4.1 单仓库 clone 配合稀疏检出

如果你只关心某一个仓库里的某个子目录,这是最省的方式。

git clone --filter=blob:none --no-checkout https://<代码托管平台>/<组织>/<仓库名>.git cd <仓库名> git sparse-checkout init --cone git sparse-checkout set <子目录A> <子目录B> git checkout <分支名>

--filter=blob:none是部分克隆,只拉提交树和元数据,文件内容按需取。--no-checkout先不检出工作区。然后用稀疏检出指定你真正要的目录。这套组合下来,一个几 GB 的仓库可能只占几百兆。

代价是每次访问没拉过的文件时会触发按需请求,离线状态下不可用。所以如果你的目标是“读完就删”,这种方式很合适;如果打算长期在上面开发,还是老实全量。

4.2 归档包与发行版本

有些场景就是想要一份静态快照:给新人做培训材料、做代码审计存档、放在离线环境里查阅。这时候归档包反而更顺手。

下载后务必校验摘要:

sha256sum <归档包名>

把结果和官方公布的值比对。别跳过这步,大文件传输损坏的概率不低,解压时才发现就晚了。解压建议用tar -xzf保留权限位,不要用图形界面的解压工具,很容易丢可执行位和软链接。

归档包的局限要说清楚:没有 .git,不能 diff、不能回退、不能看 blame。所以它只适合“读”和“存”,不适合“改”。

4.3 版本快照与每日构建产物

持续集成系统每天会跑构建,产出编译好的镜像和部分源码快照。这类产物的定位是验证和试用,不是开发基线。原因很简单:它对应的是某一次随机的提交状态,没有经过发布流程的稳定性验证,今天能跑明天可能就挂。

那它有什么用?两个场景很有价值。一是你怀疑某个问题是不是主干已经修了,拿最新快照验证一下,比从头同步快得多。二是给非技术同事演示功能,他们不需要源码,只要一个能跑起来的镜像。

如果你确实要基于快照做开发,记得用 3.4 节说的方法,把当时的 manifest 快照保存下来,否则以后没法复现。

5. 常见问题与排查实录

这部分是我这些年帮人看问题攒下来的。很多现象看着吓人,其实原因很无聊。

5.1 同步阶段的四类故障速查

现象大概率原因处理方式
卡在某个仓库长时间无输出单仓体积大或链路抖动单独repo sync <路径>,降低-j
报连接重置、early EOF并发过高或链路不稳-j4重试,或换就近镜像
提示 No space left on device磁盘满或 inode 耗尽df -h加df -i双查
报文件名非法、无法创建文件系统不支持或大小写冲突换 ext4,检查挂载参数
反复提示某仓库有本地修改上次同步中断留下脏状态确认无改动后--force-sync

第一条我遇到最多。现象是终端停在Fetching project xxx不动,一停半小时。绝大多数情况不是死锁,是那个仓库真的很大,加上网络在慢慢爬。判断方法是另开一个终端看.repo/projects/<路径>.git目录的大小有没有在涨,涨就是在下,别动它。如果十分钟一点没涨,那才是真卡了,Ctrl+C 后单独同步这个路径。

第三条特别隐蔽。磁盘还有 20G,但写入报错,八成是 inode 用完了。df -i一看 IUse% 100%,删点小文件或者换个大 inode 的文件系统。

5.2 大小写敏感与换行符:两个安静的杀手

大小写问题。这套代码里存在仅大小写不同的路径。在 macOS 默认的 APFS(大小写不敏感模式)或者 Windows 的 NTFS 上,这两个路径会被认为是同一个,后写入的覆盖先写入的,编译时报“找不到文件”,而你ls看明明在。解决办法只有两个:在 macOS 上新建一个大小写敏感的 APFS 卷,或者干脆在 Linux 上做。我建议后者,省心。

换行符问题。Git 有个core.autocrlf配置,Windows 上默认是 true,检出时把 LF 转成 CRLF。对于 C 代码无所谓,但对于 shell 脚本是致命的——带 CRLF 的脚本执行时会报bad interpreter: /bin/bash^M,那个^M就是回车符。处理方式:

git config --global core.autocrlf input

在 Linux 上设成input或者false,让 Git 原样保存。如果已经搞脏了,可以用dos2unix批量转换,或者重新检出。

5.3 编译前就报错的三类“假故障”

代码下完了,一编译就报错,很多人第一反应是“源码有问题”。我统计过,九成是环境问题。

第一类,python 版本不对。构建脚本里用了某些语法特性,python 3.11 可能能跑,3.7 就报错。反过来也有。我一般用python3 --version确认在官方建议的区间内,不在就用虚拟环境隔离,不要动系统 python。

第二类,gn 和 ninja 不在 PATH 里。有些版本的构建系统自带预编译的 gn/ninja 在prebuilts/目录下,需要先把它们加进 PATH,或者直接用项目提供的构建入口脚本(比如hb工具),它会自动处理。手动调 ninja 而没设好环境,报错信息会非常莫名其妙。

第三类,ccache 目录权限或已满。开了 ccache 但目录不可写,编译会直接失败,报错还很不直观。我习惯把 ccache 目录单独指到一块大容量盘上:

export CCACHE_DIR=/data/ccache ccache -M 30G ccache -s

-M限制最大容量,-s看命中率。第一次编译命中率接近零,第二次开始能到七成以上,提速非常明显。

6. 源码到手之后:目录结构速览与第一次构建

代码躺在磁盘上了,接下来得知道它长什么样,不然打开就是一脸茫然。

6.1 目录地图:先认路再读代码

目录作用阅读优先级
build构建框架、编译脚本、产品配置高,改配置先看这里
foundation系统基础能力,含通信、分布式、 ability 框架高
base基础库、启动、安全等底层组件中
kernel内核适配层与轻量内核实现高,做移植必看
drivers外设驱动框架与适配中
device板级配置、产品定义高,上板必看
vendor厂商定制代码视板卡而定
third_party三方开源组件低,除非要改依赖
prebuilts预编译工具链与二进制低,但要会用
out编译产物低,可以整个删掉重编
utils、test工具与测试用例中,写测试看这里

我的阅读顺序建议是:先看build里的产品配置,搞清楚一个产品是怎么被定义的;再看device里对应板卡的目录,看它拉了哪些组件;然后进foundation看系统服务怎么启动;最后才下潜到kernel。倒过来读会很痛苦,因为你不知道一个函数是被谁调起来的。

6.2 第一次构建的最小闭环

构建入口一般有两种:项目提供的封装工具(如hb),或者直接调build目录下的脚本。我用封装工具多一些,参数少,不容易错。

# 进入源码根目录 cd ~/oh_workspace # 选择产品配置 ./build.sh --product-name <产品名> # 或者用封装工具 hb set hb build -f

hb set是交互式的,会让你选当前目录对应哪个产品,选完会生成一个配置,之后hb build就按这个产品编。-f表示全量编译,不加则是增量。

第一次编译建议先跑全量,哪怕慢。原因是从零到有的过程能把所有依赖问题暴露出来,而增量编译可能因为某个模块没参与而漏掉问题。

产物一般在out/<产品名>/packages/phone/images/之类的路径下,具体以版本为准。编译成功会打印出各个镜像文件的大小。

首次编译常见失败点,按出现频率排:工具链没找到(PATH 问题)、python 依赖包缺失(缺 requests、pycryptodome 之类,按报错装就行)、磁盘空间不足(编到一半挂掉,报错信息还很不明显)、内存不足被 OOM Killer 干掉(表现为进程无声退出,看dmesg能看到记录)。

我个人在实践中形成的习惯是:第一次同步之前先跑一遍磁盘和 inode 检查,同步完立刻保存 manifest 快照,编译前先单独把 ccache 配好,这三件事花不了十分钟,但能挡掉后面绝大多数的糟心事。另外提醒一句,out目录和.repo目录千万别手贱去动,前者大不了重编,后者一动就得重新同步,那才是真的痛。

如果你只是想读代码,其实不用等全量同步完,用第 4 节说的单仓 clone 加稀疏检出,一两个小时就能开始看核心子系统的实现了。剩下那些仓库,等真正需要编译或者上板的时候再补,完全来得及。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询