第三章 源码获取与目录结构:repo工具使用、manifest仓库管理、device/vendor/hardware目录解析
好,咱们进入第三章。这一章我打算聊聊鸿蒙源码怎么拿到手,以及拿到手后那一大堆目录到底是怎么回事。
说实话,我第一次接触鸿蒙源码时,也被它的目录结构吓了一跳。跟Linux内核那种清爽的结构完全不同,鸿蒙的目录层次多、命名也怪。但别急,摸清楚规律后,你会发现它其实挺有条理。
3.1 repo工具:多仓库管理的瑞士军刀
鸿蒙的源码不是单个git仓库,而是由几百个仓库组成的。你想想看,要是每个仓库都手动git clone,那得克隆到猴年马月去?
Google搞了个repo工具来解决这个问题。鸿蒙也沿用了这套方案。说白了,repo就是一个Python脚本,帮你批量操作多个git仓库。
3.1.1 安装repo
我个人习惯把repo放在~/bin目录下。这样所有用户都能用,也方便管理。
mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
export PATH=~/bin:$PATH
嗯,这里要注意。如果你在墙内,上面的URL可能访问不了。我曾经遇到过这个问题,后来换成了国内的镜像源:
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
3.1.2 初始化与同步
拿到repo后,第一步是初始化。你需要指定一个manifest仓库的地址:
repo init -u https://gitee.com/openharmony/manifest.git -b master
-u指定manifest仓库URL,-b指定分支。我建议你第一次用的时候,先看看有哪些分支可选:
git ls-remote --heads https://gitee.com/openharmony/manifest.git
初始化完成后,执行同步命令:
repo sync -c -j8
-c表示只同步当前分支,-j8表示8个任务并行。你的机器如果核心多,可以调大这个数字。我一般设成CPU核心数的两倍。
3.2 manifest仓库:源码的“地图”
manifest仓库本身不包含代码,它只记录了一个清单——哪些仓库需要下载、每个仓库的路径是什么、对应哪个分支。
这个清单文件叫default.xml,结构大致如下:
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="origin" fetch="https://gitee.com/openharmony"/>
<default revision="master" remote="origin"/>
<project path="kernel/linux" name="kernel_linux" revision="master"/>
<project path="device/hisilicon" name="device_hisilicon" revision="master"/>
<project path="vendor/huawei" name="vendor_huawei" revision="master"/>
<!-- 还有几百个类似的条目 -->
</manifest>
每个<project>标签定义了一个仓库。path是它在工作目录中的位置,name是它在服务器上的名字。你想想看,如果没有这个清单,你怎么知道device/hisilicon这个目录对应的是哪个远程仓库?
repo sync kernel/linux device/hisilicon这样的方式。我调试某个驱动时经常这么干,省时间。
3.3 核心目录解析
同步完成后,你会看到类似这样的目录结构。我挑三个最重要的目录来讲:device、vendor、hardware。
3.3.1 device目录:板级适配的核心
device目录存放的是具体设备的适配代码。说白了,就是让鸿蒙能在某块板子上跑起来的那层代码。
它的结构通常是:
device/
├── hisilicon/ # 海思芯片相关
│ ├── hi3516dv300/ # 具体芯片型号
│ │ ├── board/ # 板级配置
│ │ ├── kernel/ # 内核适配
│ │ └── config.gni # 编译配置
│ └── hi3861v100/
├── rockchip/ # 瑞芯微芯片
└── qemu/ # QEMU模拟器
每个芯片厂商一个子目录,每个芯片型号一个子子目录。我在项目中遇到过,有时候同一款芯片会有多个开发板,这时候board目录下还会细分。
3.3.2 vendor目录:厂商定制化
vendor目录存放的是厂商的私有代码。比如华为的某些闭源驱动、UI主题、预装应用等。
它的结构跟device类似:
vendor/
├── huawei/ # 华为
│ ├── hi3516dv300/ # 对应设备
│ │ ├── hals/ # 硬件抽象层
│ │ ├── libs/ # 私有库
│ │ └── apps/ # 预装应用
│ └── common/ # 通用配置
└── hisilicon/
你可能会问,为什么要把device和vendor分开?
原因很简单:device是开源或半开源的,而vendor可能是闭源的。分开后,厂商可以只开放device部分,保留vendor作为商业机密。
3.3.3 hardware目录:硬件抽象层
hardware目录存放的是硬件抽象层(HAL)的接口定义和实现。它位于内核驱动和上层框架之间,起到“翻译官”的作用。
hardware/
├── libhardware/ # HAL核心库
├── libdisplay/ # 显示相关
├── libcamera/ # 摄像头相关
└── libsensor/ # 传感器相关
每个子目录里,你会看到include和src两个子目录。include里是头文件,定义了接口;src里是具体实现。
举个例子,显示HAL的接口定义大概长这样:
// hardware/libdisplay/include/display_hal.h
typedef struct {
int32_t (*init)(void);
int32_t (*flush)(void *buffer);
int32_t (*set_power)(int32_t mode);
} DisplayOps;
int32_t DisplayHalRegister(DisplayOps *ops);
厂商需要实现这些函数指针,然后调用DisplayHalRegister注册进去。上层框架通过这个统一的接口操作显示硬件,不用关心底层是LCD还是OLED。
3.4 目录间的协作关系
这三个目录不是孤立的。它们之间的协作关系,我用一张SVG图来展示:
从图中你能看到,device提供板级信息给vendor,vendor根据这些信息实现具体的HAL,而hardware则定义了这些HAL的接口规范。三者缺一不可。
3.5 避坑指南
最后,分享几个我踩过的坑:
- repo sync卡住:我曾经遇到repo sync到某个仓库时一直卡住不动。后来发现是那个仓库太大,网络超时了。解决办法是单独同步那个仓库:
repo sync --force-sync 仓库名 - 目录权限问题:如果你用root用户执行了repo init,那么后续操作最好也用root。否则会出现权限不足的问题。我建议统一用一个普通用户操作。
- manifest分支选错:不同版本的鸿蒙对应不同的manifest分支。比如OpenHarmony 3.2对应的是
OpenHarmony-3.2-Release。选错了会导致编译失败。 - 磁盘空间不足:鸿蒙源码全量同步大约需要30GB空间,编译后还会增加。我建议至少预留100GB。
好了,这一章就到这里。源码拿到手、目录结构搞清楚,下一步就是编译了。不过那是下一章的事,咱们先消化消化这些内容。
公众号:蓝海资料掘金营,微信deep3321