深度解析鸿蒙系统的编译流程

1.准备工作
我的本地代码是基于最新发布的openharmony 1.1.0 lts(2021-04-01)版本抓取的,代码根目录ohos1_1_0lts:
$repo init -u https://gitee.com/openharmony/manifest.git -b refs/tags/openharmony_release_v1.1.0 --no-repo-verify
$repo sync
我还有hi3861_wifiiot开发板和开发环境,如下:
在linux环境下的dececo ide下创建新工程“test_wifiiot”,在“hpm”标签下找到“@ohos/wifi_iot”,点击“install to project”选择“test_wifiiot”项目,开始下载并安装组件到项目里。
2.全编译+模块编译
2.1  ohos1_1_0lts的全代码编译
在代码根目录下执行:
ohos1_1_0lts$ hb set
[ohos info] input code path: .
ohos which product do you need?  
ipcamera_hispark_pegasus@hisilicon
-> ipcamera_hispark_taurus@hisilicon
ipcamera_hispark_aries@hisilicon
这一步完成后,会在代码根目录下生成“ohos_config.json”文件,然后就可以开始编译了:
ohos1_1_0lts$ hb build
ohos1_1_0lts$ python build.py
ohos1_1_0lts$ python build.py -p ipcamera_hispark_taurus@hisilicon
效果都是一样的。
第一次全编译了2491个文件,以后再操作上面的命令就不是全编译了,只编译需要编译的部分。
2.2  test_wifiiot项目的全编译
新下载的项目代码根目录下并没有“build.py”文件。
$ hb set
会生成“ohos_config.json”,但没有完成配置,hb build 会失败。
可以执行
$ln -s ./build/lite/build.py build.py
创建build.py的软链接,再执行
$python build.py wifiiot
就可以正常编译了。
test_wifiiot项目还可以用deveco ide进行一键编译,操作步骤见官方的文档:
https://device.harmonyos.com/cn/docs/ide/user-guides/service_introduction-0000001050166905
2.3  ohos1_1_0lts的组件/仓库/target编译
一开始,关于模块编译,我在网上搜到了:
《harmonyos单模块编译与源码导读》(link: https://harmonyos.51cto.com/posts/3094 )
这篇文章(下文简称《导读》),仔细阅读,并对比本地代码ohos1_1_0lts查看了build\lite目录下,并没有上文所提到的compile.py文件和product目录,这个可能是鸿蒙系统迭代更新版本给拿掉了。
查看ohos1_1_0lts\build\lite目录下的readme_zh.md,虽然看到增加了-t的说明:
-t [target [target ...]], --target [target [target ...]]
compile single target
但没做进一步使用说明,因为对鸿蒙的编译系统还没多少了解,我还是不大清楚如何使用操作。
官方文档“轻量和小型系统编译构建指导”:
https://device.harmonyos.com/cn/docs/develop/subsystems/oem_subsys_build_des-0000001060646620
可能没有更新,甚至没有对“-t”参数的描述。
不过官方文档中“组件描述位于build/lite/components下”这句话倒提醒了我,去里面看了一下,
原来这就是鸿蒙系统所有组件描述文件的存放位置,每个组件文件内又有多个【组件和target】的描述。
打开“applications.json”看一下,这里就有上面《导读》提到的东西:
component: camera_sample_app,
description: camera related samples.,
optional: true,
dirs: [
applications/sample/camera/launcher,
applications/sample/camera/cameraapp,
applications/sample/camera/setting,
applications/sample/camera/gallery,
applications/sample/camera/media
],
targets: [
//applications/sample/camera/launcher:launcher_hap,
//applications/sample/camera/cameraapp:cameraapp_hap,
//applications/sample/camera/setting:setting_hap,
//applications/sample/camera/gallery:gallery_hap,
//applications/sample/camera/media:media_sample
],
赶紧操作一下:
ohos1_1_0lts$ hb build -t //applications/sample/camera/launcher:launcher_hap
编译ok,不过时间还是有点长
官方文档说“组件即为gn中的编译单元,可以为静态库、动态库或可执行文件。”
编译构建流程图上也是写 hb build [component]
ohos1_1_0lts$ hb build camera_sample_app
编译ok,但感觉与target编译以及最上面hb build是一样的效果~~
我还在ohos1_1_0lts\applications\sample\camera\readme_zh.md 文档中看到对媒体子系统组件的使用说明:
单仓的编译构建,在根目录下进行单仓的构建和编译
# 开发板选择
hb set  
# 单仓构建和编译
hb build camera_lite
再查看一下build\lite\components\multimedia.json,原来单仓编译就是组件编译,而target编译,实际上看起来又与组件编译差别不大,有些组件本身就是一个target,有些组件分多个target。
component: camera_lite,
description: camera service.,
optional: true,
dirs: [
foundation/multimedia/camera_lite,
foundation/multimedia/utils/lite/hals
],
targets: [
//foundation/multimedia/camera_lite/frameworks:camera_lite
],
小结:
鸿蒙系统所有组件描述文件的存放在build/lite/components/目录下,每个组件文件内又有多个组件和target的描述。
组件/仓库编译指令:
$hb build component_name
target编译指令(可一次编译多个target,用&&连接):
$hb build -t target_name
$hb build -t target1_name&&target2_name
3.编译系统build目录结构
首先必须要仔细研读 build\lite\readme_zh.md 文档,文档上没有的地方,我这里补充一下我的理解,做成表格更容易管理。这个表格会在以后学习过程中根据新的理解做更新。
4.编译结果out目录结构
打开“os-release”文件查看:
version=openharmony 1.0
release_time=2021-04-18 07:38:53 
分别是鸿蒙系统的版本号,和当次编译(内核?系统?)的时间
5.构建系统gn+ninja
在学习鸿蒙之前,我对gn/ninja并不了解,只是知道有这么个东西,因为用不到所以也没有去学习的动力。
这几天因为想深入了解一下鸿蒙的编译系统,就通过网络资源简单学习了一下,也只是知道了个大概,我看到的资料里觉得比较好的,有如下链接:
首先最重要的“gn help ”和官方文档“the ninja build system”,随用随查。
【下文中对gn定义的关键字不再解释,请自行随时 gn help。】
《浅析鸿蒙中的gn和ninjia(一)》
link: https://harmonyos.51cto.com/posts/2972
《harmonyos 2.0研究(1) -环境搭建及编译过程分析》【强烈推荐】【本文内简称为《过程分析》】
link:https://my.oschina.net/u/2502829/blog/4613535
《#2020征文-开发板#鸿蒙liteos-a如何启动第一个用户进程init_lite》【本文内简称为《如何启动init_lite》】
link:https://harmonyos.51cto.com/posts/1998#bkwz
《gn语法和操作》
link:https://blog.csdn.net/yujiawang/article/details/72627138
为了快速理解gn+ninja是如何工作的,我自己做了一个 gnhelloworld工程来做测试和验证,源代码放在gitee上:
https://gitee.com/liangkzgitee/gnprojs.git
压缩包也添加在本文附件里,请大家按readme.txt文档操作,逐步进行跟踪和分析。
readme.txt 文档内容如下:
这是一个最简单的gn+ninja构建系统的例子工程,通过这个简单的工程和下面的操作,
学习gn+ninja构建系统是如何构建编译我们的源代码的。
原始工程目录如下:
gnhelloworld/                  #工程根目录
├──build/                    #编译构建主目录
│     └──config/               #编译相关的配置项
│             ├──toolchans/         #编译工具链相关
│             │     └──build.gn      #编译选项、链接选项等等
│             └──buildconfig.gn    #指定默认编译工具链
├──src/                      #源代码目录
│     ├──build.gn              #(*)
│     └──hello.c                #(*)源代码
├──.gn                       #gn构建系统入口
├──build.gn                  #
└──readme.txt                #(*)本文
step1:
在gnhelloworld目录下执行:gn gen out命令后,会生成一个out目录,
gnhelloworld/
├──......[略]
├──out/
│  ├──obj/
│  │      └──src/
│  │             └──hello.ninja    #编译hello.c的ninja脚本
│  ├──args.gn              #构建参数
│  ├──build.ninja          
│  ├──build.ninja.d
│  └──toolchain.ninja
├──......[略]
step2: [建议操作这步之前,先备份out目录,如: cp -r out out_bak]
在gnhelloworld目录下执行: ninja -c out,会在out目录下生成
gnhelloworld/
├──......[略]
├──out_bak/               #step1 的 out 目录的副本
│     ├──obj/
│     │     └──src/
│     │             └──hello.ninja
│     ├──args.gn
│     ├──build.ninja
│     ├──build.ninja.d
│     └──toolchain.ninja
├──out/                     #out目录有更新,见#
│    ├──obj/
│    │     ├──src
│    │     │     ├──hello.ninja
│    │     │     └──hello.o        #编译链工具根据规则生成的 .o
│    │     └──all.stamp          #
│    ├──.ninja_deps            #
│    ├──.ninja_log             #
│    ├──args.gn
│    ├──build.ninja
│    ├──build.ninja.d
│    ├──hello                 #编译后生成的可执行文件
│    └──toolchain.ninja
├──......[略]
step3:
在gnhelloworld目录下执行: 执行./out/hello,输出hello gn world!
step1做了如下工作:
1.执行gn gen 时带的参数被记录下来,生成out/args.gn【本例不带args参数】
2.找到 “.gn”文件并将其所在的目录设为“souce root”,解析该文件以获取buildconfig。
3.执行buildconfig所指向的文件buildconfig.gn,设置一个默认的编译工具链,
生成out/toolchain.ninja。
4.加载“souce root”目录下的“build.gn”文件,根据其内容加载它依赖的其它
目录下的build.gn文件,生成out/build.ninja.d。
5.根据out/build.ninja.d中各个build.gn的内容,递归解决各自的依赖关系,
解决掉依赖关系之后,就在out/obj/对应目录下,生成“.ninja”,
如例子中的“out/obj/src/hello.ninja”。
6.解决掉所有的依赖关系后,在out/目录下生成一个“build.ninja”。
step2做了如下工作:
根据上面的.ninja文件所定义的规则和依赖关系,依次编译出各自的中间文件,
最终生成可执行文件“out/hello”。
理解了上面的东西之后,你就可以开始进一步学习更复杂的东西了,可以参考gn的官方文档,
或者网络上的其它资源,自己动手做验证。
6.编译流程分析
我们repo/sync完整个鸿蒙代码后,要编译系统,一般做以下三步:
1. 首次编译,需要首先 hb set
2. 首次编译,hb build 会启动全编译
3. 修改某个文件,比如 init_lite 的main 文件main函数加行log,再次编译 hb build或者hb build init_lite。
这三步操作,鸿蒙的编译系统都做了哪些具体工作?
带着这个疑问,我希望能够抽丝剥茧,一步一步来确认,可能中间会有暂不理解的,先跳过,未来再做进一步完善。
可惜目前我对python的了解也不多,无法对相关脚本做非常详细的分析,对gn构建系统的理解也还不够深入,无法给出理想的分析结果,所以只能先把我现在知道的写下来,以后学习过程中逐步补充完善。
6.1设置环境hb set
【查看/build/lite/readme_zh.md】
设置openharmony源码目录和要编译的产品,在代码根目录生成“ohos_config.json”文件。
具体 hb set命令怎么调用/build/lite/hb/目录下的相关脚本,再结合系统环境变量$path等相关必要信息来生成这个.json文件的,懂python的可以进去看一下。
打开“ohos_config.json”文件查看:
{
root_path: /home/lkz/work/ohos1_1_0lts,
board: hispark_taurus,
kernel: liteos_a,
product: ipcamera_hispark_taurus,
product_path: /home/lkz/work/ohos1_1_0lts/vendor/hisilicon/hispark_taurus,
device_path: /home/lkz/work/ohos1_1_0lts/device/hisilicon/hispark_taurus/sdk_liteos
}
这些将作为非常重要的参数交给下一步编译使用。
6.2全编译hb build
【查看/build/lite/readme_zh.md】
编译产品、开发板或者组件。解决方案编译实现如下:
a. 读取开发板配置:主要包括开发板使用的编译工具链、编译链接命令和选项等。
b.  调用gn: 调用gn gen命令,读取产品配置(主要包括开发板、内核、选择的组件等)生成解决方案out目录和ninja文件。
c.  调用ninja:调用ninja -c out/company/product启动编译。
d.  系统镜像打包:将组件编译产物打包,制作文件系统镜像。
说的很“框架”,下面我们一步一步来看一下。
a. 检测/读取/配置所有的必要参数
包括但不限于以下列出来的几个文件:
ohos_config.json
build\lite\ohos_var.gni   定义使用于所有组件的全局变量
device\hisilicon\hispark_pegasus\sdk_liteos\config.gni  这是编译liteos_a内核所需要用到的配置
vendor\hisilicon\hispark_taurus\config.json  这是hisilicon提供的产品全量配置表:子系统、组件列表等等
通过hb build传进去的参数,比如 -n 表示编译ndk,则会将ohos_build_ndk变量由默认的false改为true。
b. 调用gn gen命令生成out目录和ninja文件
很复杂,也很简单。
复杂是中间涉及到太多的python和gn语法,我暂时无法完整理解。
简单是因为我做过了上面的gnhelloworld工程来做测试和分析,知道了框架。
更详细的分析过程,请看《过程分析》这篇文章。
下面是我对//build/lite/build.gn这个文件中的 group(ohos) 的分解,最终得到ohos的完整的依赖关系:
c. 调用ninja启动编译
这里就开始根据上一步生成的 .ninja 文件里的规则调用编译工具链来编译各目录下的源文件了,生成 .o/.a/.so/可执行文件 等等,其中.so文件会先在out\hispark_taurus\ipcamera_hispark_taurus\目录下生成,等编译完之后会转移到out\hispark_taurus\ipcamera_hispark_taurus\libs\usr\目录下,还生成了.ninja_log和.ninja_deps文件。


iPhone15最新爆料:iPhone15Pro减轻约10%
索尼回应 “已恢复对华为供货”:不评论特定客户或业务
双积分型模数转换器结构及工作原理分析
2019上海国际车展新能源车势头旺,中国厂商纷纷推出最新电动汽车
惠普创意设计PC亮相英特尔发布会 用科技升级创作体验
深度解析鸿蒙系统的编译流程
华为决定全力打造自己的AI平台
传统存储架构的局限性主要体现在哪几个方面?
线性稳压电源工作原理分析,线性稳压电源电路图详解
将 WLAN 集成至手持设备的设计
面向过程与面向对象的区别
PCB板材分类需知
ASIC芯片分类介绍及特点分析
ARM学习相关网站
英飞凌为电子身份证提供安全控制器
变频启动和工频启动的区别
新型定量微流控装置用于促进轴突转录组分析
AMD R7 5800U 的 CPU-Z 跑分,单核接近 600 分
美的集团上半年19.66%的净利润增长,从哪里实现的呢?
数据可视化开发的7个不宣之秘