云将东游,过扶摇之枝,而适遭鸿蒙。—《庄子·在宥》
前言
OpenHarmony构建工具由shell(.sh)、python(.py)、gn(.gn/.gni)、ninja(.ninja)、clang/llvm、gcc等构成。编译过程中有大量的python命令都是通过importlib.import_module方式导入(比如hb build/ hb set/ hb clean),对分析编译过程增加了一定的难度。本文以 OpenHarmony-v3.2.4-release为例,附上
OpenHarmony-v3.2.4-release下载链接。
一、编译目标分析
OpenHarmony编译完成后镜像img文件所在的目录out/{product_name}/packages/phone/images。
out/rk3568/packages/phone/images# ls -al
-rw-r--r-- 1 root root 67108864 1月 7 10:00 boot_linux.img
-rw-r--r-- 1 root root 52428800 1月 7 12:52 chip_prod.img
-rw-r--r-- 1 root root 7959 1月 7 10:00 config.cfg
-rw-r--r-- 1 root root 455104 1月 7 10:00 MiniLoaderAll.bin
-rw-r--r-- 1 root root 655 1月 7 10:00 parameter.txt
-rw-rw-r-- 1 root root 2265062 1月 7 12:52 ramdisk.img
-rw-r--r-- 1 root root 5639680 1月 7 10:00 resource.img
-rw-r--r-- 1 root root 52428800 1月 7 12:52 sys_prod.img
-rw-r--r-- 1 root root 1610608640 1月 7 12:52 system.img
-rwxr-xr-x 1 root root 4194304 1月 7 10:00 uboot.img
-rw-rw-r-- 1 root root 10323639 1月 7 12:52 updater.img
-rw-r--r-- 1 root root 1468006400 1月 7 12:52 userdata.img
-rw-r--r-- 1 root root 268431360 1月 7 12:52 vendor.img
镜像名称 | 文件格式 | 文件说明 |
---|---|---|
boot_linux.img | ext2 | linux内核 |
chip_prod.img | ext2 | 芯片相关 |
MiniLoaderAll.bin | 二进制 | 二进制镜像 |
ramdisk.img | gzip压缩cpio | ram disk |
resource.img | 二进制 | 资源文件 |
sys_prod.img | ext2 | 产品相关 |
system.img | ext2 | 系统文件 |
uboot.img | 二进制 | boot |
updater.img | ext2 | 升级文件 |
userdata.img | ext2 | 用户数据 |
vendor.img | ext2 | 厂商 |
config.cfg | 配置文件 | 配置文件 |
parameter.txt | 文本格式 | 分区文件 |
镜像文件格式可以使用file命令进行查看,如果是二进制格式则显示data类型
packages/phone/images# file boot_linux.img chip_prod.img MiniLoaderAll.bin ramdisk.img resource.img sys_prod.img system.img uboot.img updater.img userdata.img vendor.img
boot_linux.img: Linux rev 0.0 ext2 filesystem data, UUID=00000000-0000-0000-0000-000000000000
chip_prod.img: Linux rev 1.0 ext2 filesystem data, UUID=9972d61c-2903-4218-b280-c0bc5228266d (extents) (64bit) (large files) (huge files)
MiniLoaderAll.bin: data
ramdisk.img: gzip compressed data, was "ramdisk.img", last modified: Tue Dec 26 06:16:21 2023, from Unix, original size modulo 2^32 4331520
resource.img: data
sys_prod.img: Linux rev 1.0 ext2 filesystem data, UUID=0a2da78c-c4ab-44a7-b1c8-8383098758e4 (extents) (64bit) (large files) (huge files)
system.img: Linux rev 1.0 ext2 filesystem data, UUID=a2757491-4407-4139-aa26-4cac86a28ec0 (extents) (64bit) (large files) (huge files)
uboot.img: Device Tree Blob version 17, size=2560, boot CPU=0, string block size=197, DT structure block size=1964
updater.img: gzip compressed data, was "updater.img", last modified: Tue Dec 26 06:16:21 2023, from Unix, original size modulo 2^32 18057216
userdata.img: F2FS filesystem, UUID=cf1dd758-c179-4876-9518-54ee72d3f9e9, volume name "/data"
vendor.img: Linux rev 1.0 ext2 filesystem data, UUID=21ef10b2-6522-4980-bc55-79a54d4a8d0f (extents) (64bit) (large files) (huge files)
boot_linux.img为ext2格式,可以在编译电脑上进行解包,操作命令如下
/out/rk3568/packages/phone/images# mkdir boot_linux
/out/rk3568/packages/phone/images# mount -o loop boot_linux.img boot_linux
/out/rk3568/packages/phone/images# cd boot_linux
/out/rk3568/packages/phone/images/boot_linux# ls -al
total 8708
drwxr-xr-x 4 root root 4096 12月 16 13:57 .
drwxr-xr-x 3 root root 4096 12月 27 10:51 ..
drwxr-xr-x 2 root root 4096 12月 16 13:57 extlinux
-rwxr-xr-x 1 root root 2764854 12月 16 13:57 logo.bmp
-rwxr-xr-x 1 root root 2764854 12月 16 13:57 logo_kernel.bmp
drwx------ 2 root root 3354624 12月 16 13:57 lost+found
system.img为ext2格式,可以在编译电脑上进行解包,操作命令如下
/out/rk3568/packages/phone/images# mkdir system
/out/rk3568/packages/phone/images# mount -o loop system.img system
/out/rk3568/packages/phone/images# cd system
/out/rk3568/packages/phone/images/system# ls -al
total 76
drwxr-xr-x. 16 root root 4096 12月 26 14:16 .
drwxr-xr-x 4 root root 4096 12月 27 10:54 ..
lrw-r--r--. 1 root root 11 12月 26 14:16 bin -> /system/bin
drwxr-xr-x. 2 2000 root 4096 12月 26 14:16 chip_prod
lrw-r--r--. 1 root root 7 12月 26 14:16 chipset -> /vendor
dr-xr-xr-x. 2 root root 4096 12月 26 14:16 config
drwxrwx--x. 2 hxd root 4096 12月 26 14:16 data
drwxr-xr-x. 2 root root 4096 12月 26 14:16 dev
lrw-r--r--. 1 root root 11 12月 26 14:16 etc -> /system/etc
lrw-r--r--. 1 root root 16 12月 26 14:16 init -> /system/bin/init
lrw-r--r--. 1 root root 11 12月 26 14:16 lib -> /system/lib
drwx------. 2 root root 16384 12月 26 14:16 lost+found
drwxr-xr-x. 2 hxd root 4096 12月 26 14:16 mnt
drwxr-xr-x. 2 root root 4096 12月 26 14:16 proc
drwxr-xr-x. 2 root root 4096 12月 26 14:16 storage
drwxr-xr-x. 2 root root 4096 12月 26 14:16 sys
drwxr-xr-x. 2 2000 root 4096 12月 26 14:16 sys_prod
drwxr-xr-x. 9 root root 4096 12月 26 14:16 system
drwxr-xr-x. 2 root root 4096 12月 26 14:16 tmp
drwxr-xr-x. 2 root root 4096 12月 26 14:16 updater
drwxr-xr-x. 2 2000 root 4096 12月 26 14:16 vendor
ramdisk.img为压缩的cpio格式,先使用gzip进行解压缩,然后使用cpio进行挂载,操作如下
/out/rk3568/packages/phone/images# cp ramdisk.img ramdisk.img.gz //加上.gz后缀
/out/rk3568/packages/phone/images# mv ramdisk.img ramdisk.img.compile //备份原始镜像
/out/rk3568/packages/phone/images# gzip -d ramdisk.img.gz //解压缩镜像
/out/rk3568/packages/phone/images# file ramdisk.img //查看镜像格式
ramdisk.img: ASCII cpio archive (SVR4 with no CRC)
/out/rk3568/packages/phone/images# cp ramdisk.img ramdisk
/out/rk3568/packages/phone/images# cd ramdisk
/out/rk3568/packages/phone/images# mkdir ramdisk
/out/rk3568/packages/phone/images/ramdisk# cpio -idmv < ramdisk.img
.
lib
lib/libc.so
lib/libinit_module_engine.so
lib/libclang_rt.asan.so
lib/libselinux.z.so
sys
dev
storage
mnt
usr
init
system
etc
bin
bin/mknod
proc
二、编译过程分析
当在源代码目录下输入./build.sh --product-name rk3568 --ccache
都发生了什么?整个编译过程又是怎样的?本章将为您进行详细分析。源码目录下build.sh为软链接,其真实文件为./build/build_scripts/build.sh。
//build.sh
--product-name rk3568 --ccache
#检查工具链脚本build/scripts/tools_checker.py
${PYTHON3} ${source_root_dir}/build/scripts/tools_checker.py
#启动编译build/scripts/entry.py
${PYTHON3} ${source_root_dir}/build/scripts/entry.py --source-root-dir ${source_root_dir} $@
#上面命令替换成如下
#//build/scripts/entry.py --source-root-dir /usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony --product-name rk3568 --ccache
#执行结果判断
if [[ "$?" -ne 0 ]]; then #如果上条命令(build/scripts/entry.py)的返回值非0
echo -e "\033[31m=====build ${product_name} error=====\033[0m" #输出编译错误提示,\033[31m 用于控制输出显示字符颜色为红色
exit 1 #退出
fi
echo -e "\033[32m=====build ${product_name} successful=====\033[0m" #输出编译成功提示,\033[32m 用于控制输出显示字符颜色为绿色
//build/scripts/entry.py
--source-root-dir /usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony --product-name rk3568 --ccache
def check_output(cmd, **kwargs):
process = subprocess.Popen(cmd, #启动子进程
stdout=subprocess.PIPE, #子进程标准输出到管道
stderr=subprocess.STDOUT, #子进程错误输出到显示器
universal_newlines=True,
**kwargs)
#父进程继续执行下面代码,通过iter循环读取子进程输出,写入标准输出即显示器。iter循环结束条件是缓冲区为空
for line in iter(process.stdout.readline, ''):#父进程循环读取子进程管道输出
sys.stdout.write(line) #父将子进程输出写入到标准输出
sys.stdout.flush() #父将子进程输出刷新到标准输出
process.wait() #等待子进程结束
return process.returncode
def do_build(args):
build_py = os.path.join(args.source_root_dir, 'build.py')
cmd = [
'python3',
build_py,
'-p',
args.product_name,
]
if args.disable_part_of_post_build:
...
try:
#cmd=['python3', '/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/build.py', '-p', 'rk3568', '--device-type=default', '--build-variant=root']
return check_output(cmd) #调用check_output
def main():
.....
return do_build(args) #调用do_build
if __name__ == '__main__': #判断结果为真
sys.exit(main()) #调用main函数
//build.py
-p rk3568 --device-type=default --build-variant=root
def check_output(cmd, **kwargs):
process = subprocess.Popen(cmd, #启动子进程
stdout=subprocess.PIPE, #子进程标准输出到管道
stderr=subprocess.STDOUT, #子进程错误输出到显示器
universal_newlines=True,
**kwargs)
#父进程继续执行下面代码,通过iter循环读取子进程输出,写入标准输出即显示器。iter循环结束条件是缓冲区为空
for line in iter(process.stdout.readline, ''):#父进程循环读取子进程管道输出
sys.stdout.write(line) #父将子进程输出写入到标准输出
sys.stdout.flush() #父将子进程输出刷新到标准输出
process.wait() #等待子进程结束
ret_code = process.returncode
return ret_code
def set_root_path(path):
sys.path.insert(0, os.path.join(path, 'build/lite'))
module = importlib.import_module('hb_internal.set.set') #导入build/lite/hb_internal/set/set.py
return module.set_root_path(root_path=path)
def build(path, args_list):
python_executable = get_python()
cmd = [python_executable, 'build/lite/hb/__main__.py', 'build'] + args_list
#cmd=['/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/prebuilts/python/linux-x86/3.9.2/bin/python3', 'build/lite/hb/__main__.py', 'build', '-p', 'rk3568', '--device-type=default', '--build-variant=root']
return check_output(cmd, cwd=path) #调用check_output函数
def main():
root_path = os.path.dirname(os.path.abspath(__file__))
ret_code = set_root_path(root_path) #调用set_root_path函数
if ret_code != 0:
return ret_code
return build(root_path, sys.argv[1:]) #调用build函数
if __name__ == "__main__":
sys.exit(main())
//build/lite/hb/__main__.py
build -p rk3568 -device-type=default --build-variant=root
EXECV_FRAGMENT = """
import sys
import importlib
sys.path.append(sys.argv.pop())
entry = importlib.import_module("__entry__") #导入模块
sys.exit(entry.main())
"""
def main():
try:
topdir = find_top()
except Exception as ex:
return print("hb_error: Please call hb utilities inside source root directory")
python_base_dir = os.path.join(topdir, 'prebuilts/python')
if os.path.exists(python_base_dir):
python_dir = search(python_base_dir, 'python3')
python_executable = os.path.join(python_dir, 'python3')
lite_dir = os.path.join(topdir, 'build/lite')
hb_dir = search(lite_dir, '__entry__.py')
param_list = ["python3", "-c", EXECV_FRAGMENT]
for arg in sys.argv[1:]:
param_list.append(arg)
param_list.append(hb_dir)
os.environ['PATH'] = python_dir + ":" + os.getenv('PATH')
#/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/prebuilts/python/linux-x86/3.9.2/bin/python3 ['python3', '-c', '\nimport sys\nimport importlib\n\nsys.path.append(sys.argv.pop())\nentry = importlib.import_module("__entry__")\nsys.exit(entry.main())\n', 'build', '-p', 'rk3568', '--device-type=default', '--build-variant=root', '/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/build/lite/hb']
os.execv(python_executable, param_list) #第一个参数表示执行程序的路径,第二个参数为列表格式,不定长度,为想要执行程序的system argument参数,自动忽略列表中第一个参数,-c从第二个参数开始
if __name__ == "__main__": #判断结果为真
sys.exit(main()) #调用main函数
build/lite/hb/entry.py
def main():
try:
topdir = find_top()
except Exception as ex:
return print("hb_error: Please call hb utilities inside source root directory")
sys.path.insert(0, os.path.join(topdir, 'build/lite'))
parser = argparse.ArgumentParser(description='OHOS Build System '
f'version {VERSION}')
parser.add_argument('-v',
'--version',
action='version',
version=f'[OHOS INFO] hb version {VERSION}')
subparsers = parser.add_subparsers()
parser_list = []
command_set = get_hb_commands(os.path.join(topdir, 'build/lite/hb_internal/hb_command_set.json')) #json存储需要导入命令列表和命令对应的描述
for key, val in command_set.items():
parser_list.append({'name': key, 'help': val})
for each in parser_list:
module_parser = subparsers.add_parser(name=each.get('name'),
help=each.get('help'))
module = importlib.import_module('hb_internal.{0}.{0}'.format(
each.get('name'))) #将hb_internal文件夹下的build、set、env、clear、tool命令一一导入
module.add_options(module_parser)
module_parser.set_defaults(parser=module_parser,
command=module.exec_command)
args = parser.parse_known_args()
module = importlib.import_module('hb_internal.common.utils')
hb_error = getattr(module, 'hb_error')
hb_warning = getattr(module, 'hb_warning')
ohos_exception = getattr(module, 'OHOSException')
try:
if args[0].parser.prog == 'hb set' and 'root_path' in vars(args[0]):
# Root_path is topdir.
args[0].root_path = topdir
if "tool" in args[0].parser.prog:
status = args[0].command(args)
else:
#args[0]=Namespace(component=[], build_type=['debug'], compiler=['clang'], test=None, target_cpu='', compile_config=False, dmverity=False, tee=False, product=['rk3568'], full=False, ndk=False, target=[], verbose=False, sign_haps_by_server=False, patch=False, compact_mode=True, gn_args='', keep_ninja_going=False, build_only_gn=False, log_level='info', fast_rebuild=False, disable_package_image=False, disable_post_build=False, disable_part_of_post_build=[], device_type='default', build_variant='root', share_ccache='', parser=ArgumentParser(prog='-c build', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True), command=<function exec_command at 0x7f2e6c9ec160>)
status = args[0].command(args[0]) #执行build/lite/hb/hb_internal/build/build.py参数如上
except KeyboardInterrupt:
hb_warning('User Abort')
status = -1
except ohos_exception as exception:
hb_error(exception.args[0])
status = -1
except Exception as exception:
if not hasattr(args[0], 'command'):
parser.print_help()
else:
hb_error(traceback.format_exc())
hb_error(f'Unhandled error: {exception}')
status = -1
return status
if __name__ == "__main__": #importlib 调用次文件,判断为false
sys.exit(main())
build/lite/hb/hb_internal/build/build.py
Namespace(component=[], build_type=[‘debug’], compiler=[‘clang’], test=None, target_cpu=‘’, compile_config=False, dmverity=False, tee=False, product=[‘rk3568’], full=False, ndk=False, target=[], verbose=False, sign_haps_by_server=False, patch=False, compact_mode=True, gn_args=‘’, keep_ninja_going=False, build_only_gn=False, log_level=‘info’, fast_rebuild=False, disable_package_image=False, disable_post_build=False, disable_part_of_post_build=[], device_type=‘default’, build_variant=‘root’, share_ccache=‘’, parser=ArgumentParser(prog=‘-c build’, usage=None, description=None, formatter_class=<class ‘argparse.HelpFormatter’>, conflict_handler=‘error’, add_help=True), command=<function exec_command at 0x7f2e6c9ec160>)
def exec_command(args):
if len(args.product):
if '@' in args.product[0]:
product, company = args.product[0].split('@')
else:
product = args.product[0]
company = None
set_product(product_name=product, company=company)
build = Build(args.component, args.compact_mode) #创建Build类build对象
....
return build.build(args.full,
patch=args.patch,
cmd_args=cmd_args,
ninja=ninja)
build/lite/hb/hb_internal/build/build_process.py
build_process.py是编译核心流程包含:PreBuild、Patch、Preloader、self.gn_build
、self.ninja_build
class Build():
........
def build(self, full_compile, patch=False, ninja=True, cmd_args=None):
cmd_list = self.get_cmd(full_compile, patch, ninja, cmd_args)//获取编译列表
if cmd_args.get('disable_part_of_post_build'):
disable_post_build_args = cmd_args['disable_part_of_post_build']
else:
disable_post_build_args = []
try:
for exec_cmd in cmd_list:
print("\033[93m build_process.py line 153 {0}--{1} \033[0m".format
exec_cmd(cmd_args) #遍历执行命令列表
except OHOSException:
raise
except Exception:
raise
else:
if not cmd_args.get('disable_post_build'):
print("\033[93m build_process.py line 161 start postbuild\033[0m")
post_build = PostBuild(self.config)
if not cmd_args.get('disable_package_image'):
print("\033[93m build_process.py line 165 start package_image\033[0m")
post_build.package_image()
if not disable_post_build_args or 'output_part_rom_status' not in disable_post_build_args:
print("\033[93m build_process.py line 168 start romstatus\033[0m")
output_part_rom_status(self.config.root_path)
finally:
if not cmd_args.get('disable_post_build'):
if 'post_build' not in locals():
print("\033[93m build_process.py line 172 start postbuild\033[0m")
post_build = PostBuild(self.config)
print("\033[93m build_process.py line 174 start clean \033[0m")
post_build.clean(self.start_time, disable_post_build_args)
hb_info(f'{os.path.basename(self.config.out_path)} build success')
hb_info(f'cost time: {self.build_time}')
return 0
def get_cmd(self, full_compile, patch, ninja, cmd_args):
cmd_list = []
if not cmd_args.get('fast_rebuild'):
pre_build = PreBuild(self.config)
cmd_list.append(pre_build.prepare)
if patch:
patch = Patch()
cmd_list.append(patch.patch_make)
if not cmd_args.get('fast_rebuild'):
preloader = Preloader(self.config)
cmd_list.append(preloader.run)
# if full_compile is set, remove out and do full build
# if build_only_gn is set, only do gn parse
# else do incremental build.
if full_compile:
remove_path(self.config.out_path)
makedirs(self.config.out_path, exist_ok=True)
if not cmd_args.get('fast_rebuild'):
cmd_list.append(self.gn_build)
cmd_list.append(self.ninja_build)
elif not ninja:
makedirs(self.config.out_path, exist_ok=True)
if not cmd_args.get('fast_rebuild'):
cmd_list.append(self.gn_build)
return cmd_list
else:
makedirs(self.config.out_path, exist_ok=True)
if not cmd_args.get('fast_rebuild'):
cmd_list.append(self.gn_build)
cmd_list.append(self.ninja_build)
return cmd_list
def gn_build(self, cmd_args):
# Gn cmd init and execute
......
print("\033[93mbuild_process.py line 259 {0}--{1}--{2}\033[0m".format(gn_cmd, self.config.log_path,self.env()))
exec_command(gn_cmd, log_path=self.config.log_path, env=self.env())
def ninja_build(self, cmd_args):
.......
print("\033[93mbuild_process.py line 290 {0}--{1}--{2}\033[0m".format(ninja_cmd, self.config.log_path,self.env()))
exec_command(ninja_cmd,
log_path=self.config.log_path,
log_filter=True,
env=self.env())
以上注释部分打印如下
build_process.py line 153 <bound method PreBuild.prepare of <hb_internal.common.misc.PreBuild object at 0x7f3fb12ed1f0>>--defaultdict(None, {'gn': [], 'ninja': {'default_target': 'packages'}, 'log_level': 'info', 'device_type': 'default', 'build_variant': 'root'})
build_process.py line 153 <bound method Preloader.run of <hb_internal.preloader.preloader.Preloader object at 0x7f3fb12ed550>>--defaultdict(None, {'gn': [], 'ninja': {'default_target': 'packages'}, 'log_level': 'info', 'device_type': 'default', 'build_variant': 'root'})
build_process.py line 153 <bound method Build.gn_build of <hb_internal.build.build_process.Build object at 0x7f3fafbde1c0>>--defaultdict(None, {'gn': [], 'ninja': {'default_target': 'packages'}, 'log_level': 'info', 'device_type': 'default', 'build_variant': 'root'})
build_process.py line 259 ['gn', 'gen', '--args=product_path="/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/vendor/hihope/rk3568" product_name="rk3568" device_name="rk3568" target_cpu="arm" is_standard_system=true device_path="/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/device/board/rockchip/rk3568" device_config_path="/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/device/board/rockchip/rk3568" product_config_path="/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/vendor/hihope/rk3568" ace_engine_feature_enable_accessibility=true ace_engine_feature_enable_web=true enable_ohos_startup_init_feature_ab_partition=true enable_ohos_startup_init_feature_loader=true dsoftbus_feature_conn_p2p=true dsoftbus_feature_disc_ble=true dsoftbus_feature_conn_br=true dsoftbus_feature_conn_ble=true dsoftbus_feature_trans_udp_stream=true graphic_standard_feature_ace_enable_gpu=true input_feature_combination_key=true input_feature_pointer_drawing=true input_feature_interceptor=true input_feature_monitor=true input_feature_keyboard=true input_feature_mouse=true input_feature_touchscreen=true input_feature_input_device=true wpa_supplicant_driver_nl80211=true drivers_peripheral_input_feature_model=true drivers_peripheral_sensor_feature_model=true drivers_peripheral_audio_full_test_suite=true drivers_peripheral_audio_alsa_lib=false drivers_peripheral_light_feature_model=true drivers_peripheral_vibrator_feature_model=true ohos_build_type="debug" ohos_build_time="1704430722501" ohos_build_datetime="2024-01-05 20:58:42" ohos_build_enable_ccache=true build_variant="root" device_type="default"', '/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568']--/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568/build.log
build_process.py line 153 <bound method Build.ninja_build of <hb_internal.build.build_process.Build object at 0x7f7e0561f190>>--defaultdict(None, {'gn': [], 'ninja': {'default_target': 'packages'}, 'log_level': 'info', 'device_type': 'default', 'build_variant': 'root'})
build_process.py line 290 ['ninja', '-w', 'dupbuild=warn', '-C', '/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568', 'images']--/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568/build.log
def exec_command(cmd, log_path='out/build.log', **kwargs):
useful_info_pattern = re.compile(r'\[\d+/\d+\].+') #设置log过滤
is_log_filter = kwargs.pop('log_filter', False)
with open(log_path, 'at', encoding='utf-8') as log_file:
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding='utf-8',
**kwargs)
for line in iter(process.stdout.readline, ''):
if is_log_filter:
info = re.findall(useful_info_pattern, line)
if len(info):
hb_info(info[0])
else:
hb_info(line) #打印log信息,即[OHOS INFO]开头的log
log_file.write(line)
process.wait()
ret_code = process.returncode
if ret_code != 0:
if is_log_filter:
get_failed_log(log_path)
raise OHOSException('Please check build log in {}'.format(log_path))
exec_command(‘gn’, ‘gen’, ‘–args=product_path=“/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/vendor/hihope/rk3568” product_name=“rk3568” device_name=“rk3568” target_cpu=“arm” is_standard_system=true device_path=“/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/device/board/rockchip/rk3568” device_config_path=“/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/device/board/rockchip/rk3568” product_config_path=“/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/vendor/hihope/rk3568” ace_engine_feature_enable_accessibility=true ace_engine_feature_enable_web=true enable_ohos_startup_init_feature_ab_partition=true enable_ohos_startup_init_feature_loader=true dsoftbus_feature_conn_p2p=true dsoftbus_feature_disc_ble=true dsoftbus_feature_conn_br=true dsoftbus_feature_conn_ble=true dsoftbus_feature_trans_udp_stream=true graphic_standard_feature_ace_enable_gpu=true input_feature_combination_key=true input_feature_pointer_drawing=true input_feature_interceptor=true input_feature_monitor=true input_feature_keyboard=true input_feature_mouse=true input_feature_touchscreen=true input_feature_input_device=true wpa_supplicant_driver_nl80211=true drivers_peripheral_input_feature_model=true drivers_peripheral_sensor_feature_model=true drivers_peripheral_audio_full_test_suite=true drivers_peripheral_audio_alsa_lib=false drivers_peripheral_light_feature_model=true drivers_peripheral_vibrator_feature_model=true ohos_build_type=“debug” ohos_build_time=“1704430722501” ohos_build_datetime=“2024-01-05 20:58:42” ohos_build_enable_ccache=true build_variant=“root” device_type=“default”’, ‘/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568’],/usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony/out/rk3568/build.log,…)
上述命令exec_command会启动进程执行gn,gn会到根目录下寻找.gn文件,并从根.gn文件开始执行/.gn为软链接,真正文件位于//build/core/gn/dotfile.gn 内容如下,
buildconfig = "//build/config/BUILDCONFIG.gn"
# The source root location.
root = "//build/core/gn" #切换资源gn根目录为//build/core/gn,自动会执行根目录下BUILD.gn
# The executable used to execute scripts in action and exec_script.
script_executable = "/usr/bin/env"
//build/core/gn/BUILD.gn
内容如下,
print("root_out_dir=$root_out_dir") //编译log中的打印[OHOS INFO]
print("root_build_dir=$root_build_dir")
print("root_gen_dir=$root_gen_dir")
print("current_toolchain=$current_toolchain")
print("host_toolchain=$host_toolchain")
# run loader
import("//build/core/gn/loader.gni")
# gn target defined
if (product_name == "ohos-sdk") {
group("build_ohos_sdk") {
deps = [
"//build/ohos/ndk:ohos_ndk",
"//build/ohos/sdk:ohos_sdk",
]
}
} else {
group("make_all") {
deps = [
":make_inner_kits",
":packages",
]
if (is_standard_system) {
# Lite system uses different packaging scheme, which is called in hb.
# So skip images for lite system since it's the mkimage
# action for standard system.
deps += [ ":images" ]
}
}
group("images") {
deps = [ "//build/ohos/images:make_images" ]
}
group("packages") {
deps = [ "//build/ohos/packages:make_packages" ]
}
group("make_inner_kits") {
deps = [ "$root_build_dir/build_configs:inner_kits" ]
}
group("build_all_test_pkg") {
testonly = true
deps = [
"$root_build_dir/build_configs:parts_test",
"//test/testfwk/developer_test:make_temp_test",
]
}
group("make_test") {
testonly = true
deps = [
"//build/ohos/packages:build_all_test_pkg",
"//build/ohos/packages:package_testcase",
"//build/ohos/packages:package_testcase_mlf",
]
if (archive_component) {
deps += [ "//build/ohos/testfwk:archive_testcase" ]
}
}
}
命令行打印log如下
[OHOS INFO] root_out_dir=//out/rk3568
[OHOS INFO] root_build_dir=//out/rk3568
[OHOS INFO] root_gen_dir=//out/rk3568/gen
[OHOS INFO] current_toolchain=//build/toolchain/ohos:ohos_clang_arm
[OHOS INFO] host_toolchain=//build/toolchain/linux:clang_x64
完整脚本调用过程如下
./build.sh --product-name rk3568 --ccache #--ccache可以不带,默认使用ccache加速编译
build/scripts/tools_checker.py
build/scripts/entry.py --source-root-dir /usr/src/openharmony/OpenHarmony-v3.2.4-Release/OpenHarmony --product-name rk3568 --ccache
build.py -p rk3568 --device-type=default --build-variant=root
build/lite/hb/__main__.py build -p rk3568 -device-type=default --build-variant=root
build/lite/hb/__entry__.py
build/lite/hb/hb_internal/build/build.py
build/lite/hb/hb_internal/build/build_process.py
PreBuild #hb_internal.common.misc.PreBuild,1.设置ccache大小;2.备份上次编译log build.log->build.<上次修改时间>.log
#并在屏幕输出第一行以[OHOS INFO]开头的log,为执行ccache -M 100GB命令自动输出
#[OHOS INFO] Set cache size limit to 100.0 GB
Patch #默认不执行,代码路径build/lite/hb/hb_internal/build/patch_process.py
Preloader #hb_internal.preloader.preloader.Preloader
#创建out/preloader/rk3568文件夹,生成syscap.json,SystemCapability.json,part.json,part_config.json,feature.json,build_gnargs.prop,exclusion_module.json,build.prob,platforms.build, 创建system文件夹生成对应文件
self.gn_build #hb_internal.build.build_process.Build
self.ninja_build #hb_internal.build.build_process.Build
PostBuild #packiamge执行镜像打包
out_put_part_rom_status #统计各部件大小
clean #统计ccache命中率信息、ninja trace、计算c语言代码覆盖率
总结
OpenHarmony编译过程已经分析完毕,如果想了解编译框架,可以看我的另一篇文章《OpenHarmony源码学习之编译构建》。