type
status
date
slug
summary
tags
category
icon
password
说在前面
我最终使用的OH版本为OpenHarmony-v4.0-Release,使用的device为qemu-arm-virt,Linux的配置为默认配置。
OH源码下载
在正式开始做任务之前,先看看在编译的过程中OH是如何给Linux打上补丁的,感觉能从中能借鉴一点东西。
gitee配置
gitee配置主要参考:
这里主要是注册一下gitee,然后配置一下gitee的shh公钥,这些配置再github上经常搞,在上面的链接中也给出了gitee官网文档中的操作方式,这里就不再赘述了。
git-lft安装
参考:
按道理来说在这个之前还需要安装一下git客户端的,之前已经安装过了,这里直接跳到安装git-lft
git-lft的官方介绍是:
Git LFS is a command line extension and specification for managing large files with Git
主要就是说git-lft是一个git的扩展,是使用git管理大文件的一个规范。
这是由于OH真的非常大。。。
这里的42.7G仅仅是软件包。通常情况下,还会包含一些配置文件,所占用的磁盘空间应该
- 添加packagecloud仓库
由于我是ubuntu系统,因此如果想要直接使用apt安装的话,首先需要先添加一下packagecloud的仓库,以便能获取到git-lft。需要在终端中输入下面的指令:
- 安装
这里就可以直接使用apt进行安装了。使用以下指令:
到这里就安装好git-lft了
安装码云repo工具
在这之前本来还需要使用下面的指令配置一下git的信息的:
但是同样因为之前使用github的时候已经配置过了:
因此这里也不再重新配置了,直接开始安装码云repo工具。
需要输入以下的指令:
获取OH源码
在OH gitee仓库中,提供了两种方法
- 从版本分支获取源码
使用下面的指令:
从指令中能看出来是从OpenHarmony-5.0.1-Release分支中获取代码,这个跟git clone某个特定分支的时候是一样的
- 从tag获取源码
使用下面的指令:
同样可以看出来是从OpenHarmony-v5.0.1-Release这个tag中获取源码。
由于通常情况下tag是表示发布版本,可能出现的问题会比较少,所以我这里采取第二种方式获取源码。
另外需要注意的是,我在这里的init中加上了
--depth=1
(实际上不加也行,只不过磁盘空间要大一点),目的是为了在将远程仓库同步到本地的时候可以只下载远程仓库默认分支中的最新一次的提交记录,以减少获取源码对磁盘的消耗(OH吃磁盘吃的太多了。。。)在首次init pull的过程中,会提示需要将'gitee.com' (ED25519)添加到已知host中。这里的配置相当于是让我的机器信任gitee.com,而上面在gitee中配置ssh密钥相当于是让gitee信任我的机器。
在后面进行源码编译并在qemu中运行的过程中,使用的v5版本好像一直在出问题,所以我后面还试了试网上一些博主正在使用的v4版本,以便能够复现他们的结果。
OH的补丁
网络问题
参考:
在编译OH之前遇到了一个网络问题:虚拟机无法联网(ssh连接无法建立),使用
ifconfig
指令也找不到ens33网卡的相关信息,但是使用ip a
指令可以找到该设备,但是该设备的信息为:在参考的文章中指出:
这种情况出现一般是在上次正常使用可以上网,之后挂起或关机后再重启系统出现,初步判定与网卡状态文件记录有关
这里的描述与我的状态很像,因此选择尝试一下这篇博客的方法——删除网卡状态管理文件
在这里涉及到一个东西——NetworkManager,这个东西负责自动管理网络设备(包括开机自启动)。
首先输入指令:
然后删除网卡状态管理文件:
最后重启NetworkManager服务即可:
重启之后就可以在虚拟机的右上角发现网络图标了:
但是这个配置好像重启了之后还是会出现问题。在第二篇博客中,讲到了如何实现NetworkManager的开机自启动。
首先先查看NetworkManager的配置文件:
做出如下修改:
接下来需要检查NetworkManager是否被启用了:
如果返回的是disabled的话,就表示目前NetworkManager没有被启用,那么需要输入以下指令,以启动NetworkManager:
接下来可以再次检查NetworkManager是否被启用,如果返回的是enabled的话,就可以实现NetworkManager的开机自启动了~
ohos.xml文件
在OH的manifest中,只有几个xml文件。这些xml文件大多是用来指定OH各个模块所在的仓库的,以便在获取源码的时候能够根据配置找到相关的仓库并clone。
在ohos目录下,有一个xml文件涉及到了任务中提到的patch:ohos.xml。
正如上面所说,
ohos.xml
文件用于定义和管理项目的各个子模块和仓库。在ohos.xml中,有一行:
这个就是OH的补丁组件,他将会在编译OH的时候,为Linux LTS(4.19.y / 5.10.y)打上一些补丁,以支持OpenHarmony项目开发。这里稍微介绍一下这一行:
- name:指定了这个project(或者说是子模块)的仓库名称,因此可以直接在OH社区中搜索"kernel_linux_patches",以查看补丁仓库
- path:用于指定在最终获取到本地的源码中,这个project的路径。
- groups:groups属性用于对项目中的各个子模块进行分组管理。通过分组,就选择性地同步和编译某些特定分组的模块,从而更灵活地管理项目的构建过程。
- default:默认分组
- ohos:small:小型设备分组
- ohos:standard:标准设备分组
- ohos:chipset:芯片组分组
- ohos:system:系统分组
这个给我的感觉有点像是Rust中的feature,也就是条件编译。
这里的patch子模块就位于五个组内:
大概就是针对不同的应用场景来分组吧。感觉这个分组不是重点,这里就不深究了。
OH Patch
参考:
下面我将按照我查看文件的顺序来详细介绍OH是如何在编译的过程中为Linux内核打上补丁的。
Kernel.mk
文件路径:kernel/linux/build/kernel.mk
在仓库的Readme中有描述:
针对不同的芯片,各厂商合入对应的板级驱动补丁,完成对OpenHarmony的基线适配。 内核的Patch组成模块,在编译构建流程中,针对具体芯片平台,合入对应的架构驱动代码,进行编译对应的内核镜像。所有补丁来源均遵守GPL-2.0协议。
在补丁仓库中有许多的patch,这里仅仅以HDF补丁为例。在补丁代码仓库中对HDF补丁的描述为(以linux-4.19为例):
linux-4.19 HDF patches
从这里可以看出这个补丁是和内核强相关的。在其他的资料中介绍到:
linux-4.19 HDF patches 是指针对 Linux 4.19 内核版本的补丁,这些补丁专门用于支持 OpenHarmony 的硬件驱动框架(HDF)
在补丁代码仓库中讲到了补丁合入的方式:
在kernel/linux/build仓中,按照kernel.mk中HDF的补丁合入方法,合入不同内核版本对应的HDF内核补丁
这里提到了kernel.mk文件。我感觉只要知道这个Makefile是用来编译内核的(包括打上补丁等等)。在这个Makefile中只有一个伪目标:
build-kernel
,而该伪目标又依赖于$(KERNEL_IMAGE_FILE)
。$(KERNEL_IMAGE_FILE)
没有依赖文件。为了实现这个伪目标将会执行若干编译内核时需要的指令。因此Makefile中最关键的部分的结构为:在
$(KERNEL_IMAGE_FILE)
文件中,有下面的编译指令:这一个编译指令就负责对OH使用的内核打上HDF补丁。这里详细介绍一下这一条指令:
- hide:用于表示是否需要隐藏本条指令。
如果需要隐藏指令的话,需要将hide设置为“@”
- OHOS_BUILD_HOME:这个变量被设置为
OHOS_BUILD_HOME := $(realpath $(shell pwd)/../../../)
,即当前目录的父父父级目录,也就是当前OH的项目目录。
接下来的部分是为
patch_hdf
脚本传递四个参数:- OHOS_BUILD_HOME:上面解释过,这里不再赘述
- KERNEL_SRC_TMP_PATH:这个 Makefile 变量用于指定内核源码的临时路径。如果不是要编译OH标准系统,那么
KERNEL_SRC_TMP_PATH
将会被设置为$(OUT_DIR)/kernel/${KERNEL_VERSION}
;反之则将被设置为KERNEL_SRC_TMP_PATH := $(OUT_DIR)/kernel/src_tmp/${KERNEL_VERSION}
在构建过程中,内核源码会被复制到这个临时路径进行编译和打补丁等操作。这样做的好处是可以保持原始内核源码的干净和完整,同时在临时路径中进行各种修改和构建操作。因此在编译的过程中其实还需要很多的磁盘空间。在后面编译qemu镜像的时候,从结果来看,应该build_type是standard
- KERNEL_PATCH_PATH:这个变量用于指定内核补丁所在的路径。其被设置为:
KERNEL_PATCH_PATH := $(OHOS_BUILD_HOME)/kernel/linux/patches/${KERNEL_VERSION}
- DEVICE_NAME:用于指定设备的名称,如
hispark_phoenix
,以便针对不同的设备进行特定的操作。
hispark_phoenix是海思半导体的一款开发板。
patch_hdf.sh
文件路径:drivers/hdf_core/adapter/khdf/linux/patch_hdf.sh
从Makefile中可以看到,
patch_hdf.sh
文件位于:$(OHOS_BUILD_HOME)/drivers/hdf_core/adapter/khdf/linux
路径中,并且在编译内核的时候我们向改脚本传递了四个参数。下面结合脚本的内容,通过在脚本中打注释的方式,详细介绍该脚本文件的功能:从上面的分析中可以看出,这个文件的主要工作是准备补丁需要的相关资源,然后再使用
patch -p1
指令打上补丁。hdf.patch
文件路径:kernel/linux/patches/linux-4.19/common_patch/hdf.patch
.patch文件通常情况下由git自动生成。这里截取一段hdf.patch文件中的内容进行分析(使用注释的方式):
build.sh
文件路径为:build.sh
在上面是从kernel.mk向下看,现在再尝试从根目录的build.sh脚本向下看,看看kernel.mk是如何被执行的。
在构建OH的时候通常只需要运行项目根目录中的build脚本即可,因此一定有一个调用关系,是从项目的build.sh一直到打补丁的。把这个搞清楚感觉也有利于搞清楚整个项目的结构。
我有这个想法是因为,在补丁仓库中使用的指令为:
在这里指定了一个命令行参数:
--build-target build_kernel
,也就是表示当前编译的目标为:构建内核,给我的感觉是通过指定这个--build-target,顶层的shell脚本就将向下调用构建内核的脚本,进而实现打补丁等操作(当然我觉得就算不指定这个参数,也会去调用脚本构建内核)。为了验证这个猜想,所以我打算看看OH中的sh脚本和py脚本的调用关系。这里先要回忆一点点shell脚本的知识:
- $*:$*将所有参数作为一个单一的字符串处理,参数之间用第一个字符的值(通常是空格)分隔。
- $@:$@将每个参数作为独立的字符串处理,因此使用 $@ 时,每个参数仍然是独立的。
在build.sh脚本中,$*主要用于进行正则表达式匹配,以检查是否有ohos-sdk参数以及-no-prebuilt-sdk参数。
而$@在build.sh脚本中,仅仅使用在下面这一段脚本中(同样详细阅读一下这一段代码):
在整个shell脚本中只有在这个地方使用到了build.sh的命令行参数。而且经过我的检查,在我使用的编译指令的条件下,flag变量应该为true。因此最终所有的命令行参数都将被传递到一个py脚本中,即指令:
main.py
文件路径:/build/hb/main.py
main.py中的main函数:
在上面的build.sh脚本中可以发现,传递给main.py脚本的第一个命令行参数为build,因此在main函数中
module_type
变量即为build。因此在这里将会执行Main
类的_init_build_module()
方法:从这里可以看到,
_init_build_module()
函数对一个OHOSBuildModule
模块进行了初始化,并将其返回。。后续在main函数调用该方法之后,使用module
变量存储了方法的返回值。后续就调用了module
的run
方法,以构建该OHOS。ohos_build_module.py
路径:build/hb/modules/ohos_build_module.py
OHOSBuildModule
在ohos_build_module.py文件中被定义。上面我们知道在main函数中调用了该类的run
方法,其run
方法的定义如下:在这里可以发现,
OHOSBuildModule
调用了其父类的run
方法,接下来仅仅是进行了一些异常处理。从OHOSBuildModule
模块的函数签名中可以知道该类的父类为BuildModuleInterface
:build_module_interface.py
文件路径:build/hb/modules/interface/build_module_interface.py
BuildModuleInterface
被定义在build_module_interface文件中。其run方法的定义为:在这里调用了四个方法以及一个finally(剩余的部分都是错误处理,这里就不再赘述了)。这里重点关注函数
_target_generate()
由于目前如果只是想了解整个构建的过程(或者说脚本的调用顺序的话),应该只需要看
_target_generate()
函数即可,因此这里就暂时先不看其他的四个函数了。。。这里需要稍微介绍一下GN是什么。GN全称是General Ninja,是一个用于生成和管理构建系统的工具,主要用于生成 Ninja 构建文件(因此在run方法中可以发现,gn函数是在ninja函数之前被调用的)。GN 的主要目标是提供一个更高级别的描述语言,使得生成 Ninja 构建文件更加容易和灵活(目前我对GN作用的理解就是为ninja进行预处理工作?)。
在后面会出现.gn文件。.gn文件需要使用 GN 工具进行处理。.gn文件是 GN 构建系统的配置文件,定义了项目的构建配置和构建规则(所以我的感觉是.gn文件的作用可能跟Makefile差不多?也是用来构建项目的)
_target_generate()
函数的定义如下:因此在调用该函数时,将调用实例化的方法,也就是
OHOSBuildModule
中定义的方法:在这里调用了
target_generator.run()
方法。在OHOSBuildModule
初始化函数中可以找到target_generator
的类型:build_file_generator_interface.py
路径:build/hb/services/interface/build_file_generator_interface.py
build_file_generator_interface.py文件中定义的run方法为:
同样我们需要找到实例化的方法。在
main.py
中进行初始化操作时,为target_generator
参数传递的值为Gn()
函数的返回值,即main.py
中的变量generate_ninja
gn.py
路径:build/hb/services/gn.py
Gn()
函数是在gn.py
文件中定义的。从Gn
类的定义上也能看出来其继承了BuildFileGeneratorInterface
:因此在执行
target_generator.run()
时将执行Gn的run方法:execute_gn_cmd
函数定义如下:由于
execute_gn_cmd
函数的传参在run
方法中已经被硬编码为了CMDTYPE.GEN
,因此在接下来将执行_execute_gn_gen_cmd
方法:到这里其实就很熟悉了,这个函数负责使用gn对项目进行初步构建(使用
SystemUtil.exec_command
函数),并输出日志信息。比如这里的'GN phase failed', '3000'
,'Excuting gn command:
等我见过好多次了。。。其中
Excuting gn command
日志信息可以在build.log日志中查看:此外,在out_dir下还有一个文件
args.gn
,其以更可读的方式记录了Gn所使用的参数:注意到这里有一个gn参数
product_config_path
,其指定了一个目录。在该目录下只有两个文件:config.json
与qemu_run.sh
。其中qemu_run.sh
是用于在qemu中运行镜像的,而config.json
文件自然就是对项目构建进行配置的文件(需要编译的子系统也是在config.json
文件中设置的)。与在OH根目录中生成的ohos_config.json
文件对比,不难发现ohos_config.json
文件就是根据config.json
生成的。gn具体是如何使用这个json文件的后面可以仔细研究一下
.gn文件依赖链
在
config.json
文件中,有一个配置:需要注意的是,这个配置文件对Gn是可见的(因为该文件所在的目录被作为Gn的参数进行传递了)。在
device_build_path
指定的目录下,有一个.gn文件:其定义了一个gn group,并且设置了三个依赖,这里我们主要关注
chipset_files
这是因为该dep与构建内核相关
我们进入
device/qemu/arm_virt/linux/chipset
目录之后,同样会有一个.gn文件:在这个.gn文件中不难发现,chipset_files依赖于linux_kernel。
我们在
kernel/linux/build
路径中同样能找到一个.gn文件,其中linux_kernel的定义为:linux_kernel依赖于build_kernel,而build_kernel就在当前.gn文件中被定义:
build_kernel在这里被定义为一个action,并将script指定为
build_kernel.sh
。因此当执行build_kernel这个action的时候,将会调用build_kernel.sh
脚本构建内核。build_kernel.sh与kernel_module_build.sh
路径:kernel/linux/build/build_kernel.sh && kernel/linux/build/kernel_module_build.sh
在
build_kernel.sh
脚本中,有如下一行脚本指令:即在
build_kernel.sh
脚本中将会调用./kernel_module_build.sh
。在
kernel_module_build.sh
中,有一行code:该指令将调用make工具,并指定使用kernel.mk文件作为Makefile来执行构建过程。
这里的-f选项就是用于指定Makefile文件的,如果不使用-f选项的话就将寻找默认的、以Makefile命名的文件。
另外可能会注意到,这里只使用了make指令,而没有指定构建的目标。由于各个目标在项目中的依赖关系,因此执行make指令的时候将会尝试构建build-kernel伪目标。
至此就调用到了
kernel.mk
文件,进而就实现了打补丁等操作。我们也可以在日志中稍微检查一下是否会运行到打补丁的
patch_hdf.sh
脚本。我在patch_hdf.sh
中增加了一下代码:并在
kernel.mk
中将执行patch_hdf.sh
脚本的hide去掉:在build.log中能找到该输出:
编译OH
参考:
开发者文档:
开发者文档中涉及编译配置的部分:
qemu-x86-64构建Readme:
qemu-riscv64构建Readme:
qemu-riscv32构建Readme:
qemu-arm-linux构建:
配置编译环境
在这里主要尝试一下手动配置环境吧
按照官方文档上手动配置的步骤,需要先通过Samba配置Windows和Linux之间的共享目录,并且还要使用apt安装一堆软件包(也就是安装库和工具集),这里就不再赘述了。
安装编译工具HB
由于我使用的是最新版本的OH,因此在安装编译工具这一步主要是安装hb。在这里先稍微介绍一下hb这个工具吧。
hb的全称在安装hb的时候可以看到为:ohos-build。hb是 OpenHarmony 项目中的一个构建工具,用于简化和管理构建过程。它是 OpenHarmony 的命令行工具,提供了一系列命令来配置、构建和管理 OpenHarmony 项目。
接下来正式开始安装编译工具,在OH项目目录中输入以下指令:
指令执行结束之后会弹出一个Warning:
这里就提到hb被安装在路径:
/home/shinuohui/.local/bin
,但是这个路径不在环境变量中。首先先试用vscode打开终端配置文件:通常情况下是~/.bashrc文件。
然后加入:
最后使用指令:
更新环境变量即可。
接下来可以使用指令
hb help
指令检查是否可以使用hb工具:未来如果需要卸载hb工具的话,可以使用指令:
实际上后面将使用build脚本进行编译,不需要hb:
在这里提到了有两种构建方法,一种是使用hb,另一种是使用build.sh脚本
源码构建(qemu_x86_64)
这里主要还是使用build脚本来构建源码:
构建过程中的问题记录在了杂记部分。构建结束之后,即可得到OH镜像文件:
运行镜像(qemu_x86_64)
首先可以先检查一下out目录中的镜像文件是否正确,使用指令:
我的输出为:
不难发现这里的镜像文件跟参考博客中记录的是一样的。
接下来就可以使用下面的指令,在qemu中运行该镜像了:
但是不幸的是,在qemu中运行镜像时出现了问题:
我在OH的device_qemu仓库的issue中找到了与我相似的情况:
但是这个issue目前好像没有解决(或者说评论中给出的解决方式我有点没看懂。。。)。因此这里我选择换一个CPU架构尝试一下,这里我选择的是qemu-riscv64
源码构建(qemu-riscv64)
在这里同样使用build脚本尝试构建:
但是在编译的过程中我遇到了CODE 3000,这个问题在网上我没有找到解决方式,这里给出部分报错信息:
希望后面有人能解决一下吧。。。
源码构建(qemu-riscv32)
选择qemu-riscv32还有一个重要的原因是因为网上能查到有的博主成功实现了在qemu中运行qemu-riscv32。。。
在构建qemu-riscv32之前,需要先安装gcc-riscv32,安装包在Readme中已经给出了,只需要下载解压之后配置环境变量即可,这里就不再赘述了。
在构建qemu-riscv32时,Readme中给出的方式是使用hb工具进行构建:
然后选择
mini
之后,再选择ohemu
下的qemu_riscv_mini_system_demo
选项。然后使用指令:即可开始编译。
qemu-riscv32 demo的编译过程异常的顺畅啊。。。
运行镜像(qemu-riscv32)
在过程中也遇到了一些问题,也记录在“一些问题”部分了。这下终于能看到OHOS的终端了ww:
这里是使用的qemu-riscv32,但是在运行过程中我发现很多指令都没办法使用,比如uname、grep等指令,甚至使用ls都会一直报错。。。
后面尝试了在qemu中运行OHv4 x86_64,但是最后在运行的时候还是Kernel Panic了。。。
但是在运行之后,查看hb相关的配置(也就是
ohos_config.json
文件),发现OH在qemu-riscv32上运行时使用的内核为liteos_m
,而不是linux
。这是因为编译时指定的device_path
为:而
riscv32_virt
下又只有liteos_m
,无法指定linux
了。在其他qemu支持的设备中,我发现只有x86_64
和arm_virt
支持使用linux内核,而上面我已经尝试过了在qemu-x86_64中运行OHv4,仍然会出现kernel panic,因此这里我打算再尝试一下arm-virt
源码构建(qemu-arm-virt)
参考Readme,在OH根目录中输入:
这里我额外加上了sudo是因为我直接使用脚本编译的时候会出现权限不够的情况。这里实际上是在构建两种镜像,在Readme中指出:
qemu-arm-linux-min表示部件最小集合的产品。 qemu-arm-linux-headless表示在最小集合基础上,支持无屏幕的用户程序框架部件集合的产品。
编译结束之后,使用以下指令运行镜像:
接下来就可以进入OHOS的控制台了(只不过是不是会刷一些日志出来。。。),并且使用
uname -a
指令输出了以下信息:内核为Linux。
Linux RT Patch
参考:
补丁地址:
在已经了解了OH打补丁的流程之后,如果要同时打上Linux RT的补丁就变得简单了。这里我采取一个比较简单的方法:
- 将Linux RT补丁文件拷贝到HDF补丁的目录下
- 在patch_hdf.sh脚本中模仿打HDF补丁的操作额外增加一行shell指令:
RT_PATCH_FILE变量的值被设定为Linux RT补丁的路径
在对补丁进行一定的修改之后,编译并运行OH,再次使用
uname -a
指令,输出的信息为:在这里可以看见,与上面未打RT补丁时相比,这里额外增加了rt信息,说明Linux RT补丁应该成功打上了。
这里还没有测试RT Linux的其他的功能,因为之前没有用过RT Linux。。。后面有时间的话可以补上对RT-OH的相关功能测试
最后我将我修改后的Linux RT Patch和Linux官方的Linux RT Patch都上传到了下面的github仓库中:
大功告成~
杂记
- 安装qemu-riscv32,依次使用指令:
这里的
--target-list
选项用于指定构建的目标,这里就是只要构建qemu-riscv32。在编译的时候遇到了一点点问题,大概就是有一些符undefined的符号,通过查找网上的资料发现,是以为gcc和g++版本不匹配导致的。这是我的一个历史遗留问题,在我的~/.zshrc中有这两行配置:
一些问题
- 在使用repo+ssh下载源码的时候出现报错:
这是因为使用repo进行源码获取时,需要使用到这个Python包。而Python查找包的时候会在PYTHONPATH环境变量中进行查找,因此这里可能需要配置一下这个环境变量。
需要特别注意的是,可能会因为有多个Python,而每一个Python又都有一个requests包,这样就可能由于Python解释器和requests包版本不对应(这可能会导致requests包无法识别?),进而导致repo中出现No module named requests。
但是这个又不好直接去删掉某一个Python,感觉一个比较好的方式就是配PYTHONPATH环境变量,让其包含所有Python使用的包的路径。在我的虚拟机上就是有两个Python包路径的:
- 在使用
repo sync -c
指令时出现报错:
出现这个问题的原因主要是磁盘空间不够了,使用gparted增加了磁盘空间之后就可以正常同步代码了。
- 使用指令
./build.sh --product-name qemu-x86_64-linux-min --ccache --jobs 4
出现报错:
这个报错还是很明显的,也就是这里说的,要先执行
build/prebuilts_download.sh
。- 同样是执行build.sh脚本时报错
在报错信息中是说可以使用'--no-prebuilt-sdk'来跳过ohos-sdk的编译,但是我因为害怕后面会因为缺少这个东西报一些错误,我就在网上找了一个编译ohos-sdk的方法:
即在终端中输入指令:
- 编译ohos-sdk时报错:
从这里可以看出来,是因为缺少一些依赖,进而导致了ohos-sdk编译失败。在报错信息中可以找到,我没有把shell修改为bash。
这里就只需要按照报错信息中提示的,将dash关闭即可。关闭dash之后,可以使用以下指令查看是否修改成功:
如果输出:
就表示成功将shell修改为bash了。
在输出的信息中还有提到:
这些工具在安装库和工具集步骤中使用的指令中已经包含了,但是没有成功安装,不知道是不是因为没有改成bash的原因。。。这里尝试重新执行安装指令,之后,就没有缺少依赖了。
- 同样是在编译ohos-sdk时报错:
参考:
通过指令:
在我的系统中有这些libffi:
既然找不到libffi.so.6,那就流氓一点通过符号链接将libffi.so.6链接到我目前已经有的libffi.so上:
- 编译ohos-sdk时报错:
网上说的是使用sudo,但是我试了好像没什么用。。。但是后面好像就发现可以直接编译,好像不用sdk了(突然就好了不太明白是为什么。。。)
- 执行build脚本构建qemu标准系统报错:
从这里的原因可以看出
/home/shinuohui/code/OH/device/qemu/x86_64_virt/linux/ohos.build
路径中的子系统名称似乎是错误的。对该文件做出如下更改:即可解决这个问题。
在上面的参考博客中还有一种解决方式:
我的解决方案是修改build/compile_standard_whitelist.json,将报错的device/qemu/x86_64_virt/linux/ohos.build加到bundle_subsystem_error白名单
我在编译qemu-riscv64的时候尝试使用了这种解决方式:
但是修改之后在编译的过程中遇到了CODE 3000报错,我在想是不是这个解决方式有问题,因此我又重新编译了一遍qemu-x86-64,并且使用修改
build/compile_standard_whitelist.json
的方式,来解决CODE 2014报错。我发现在编译qemu-x86-64时使用修改白名单的方式解决CODE 2014报错是可行的(但是最终生成的镜像还是无法在qemu中运行)- 执行build脚本构建qemu标准系统报错:
报错信息中给出的信息为:check
/home/shinuohui/code/OH/out/preloader/qemu-x86_64-linux-min/parts.json
。这个文件用于指定在构建 qemu-x86_64-linux-min
目标时需要包含的模块。构建系统会读取这个文件,并根据其中列出的模块来构建和集成各个组件。从第一行报错信息中可以发现,在比对components_name和subsystem_compoents_whitelist_info时出现了错误。通过查看parts.json
文件,发现在parts.json
文件中定义了上述的三个组件(也就是表明在构建qemu-x86_64-linux-min时需要这三个组件),但是在白名单中没有指定这三个模块(也就是表明在编译的过程中将不会包含这三个模块),因此按照上面博客中提到的解决方式,在白名单中增加这三个模块:- 编译时报错
这个报错是因为我笨蛋了,指令中的product name参数应该为
--product-name
而不是--product_name
- 在使用
hb set
时出现报错(hb是为了尝试使用qemu-riscv32)
参考:
参考博客中给出的原因是:
我下载的python解释器版本是3.10,python3自3.10版本后对requests库有进行调整,collections中不能直接调用Mapping、MutableMapping
解决方式是在
/usr/lib/python3.10/collections/__init__.py
文件中增加:- 使用
./qemu_run
指令运行qemu-riscv32 demo时报错:
也就是说我的qemu好像是不支持
-vnc
这个选项的。我通过./qemu_run --help
指令发现,可以使用指令./qemu_run -l
指令启动qemu并且不使用VNC,但是使用这个指令之后就会导致qemu_run脚本运行不正常,具体的表现就是终端没有任何输出(我也不知道这个是不是正常的情况)。对于这个问题,我最终的解决方式是使用OH仓库中给出的qemu6.2。- 运行riscv32镜像报错:
呃呃,在别人运行OHv4的时候就没有这个报错。。。希望后面有人能解决吧。。。
后面我在qemu-riscv32上运行OHv4时也出现了类似的报错:
解决方式参考:
对
vendor/ohemu/qemu_riscv32_mini_system_demo/config.json
文件做出如下修改:修改之后重新编译镜像,即可在qemu中成功运行。
可能在qemu中运行OHv5时遇到的“not a posix thread”问题也可以通过这种解决方式解决(但是我还没有尝试过。。。)
- 编译OHv4、qemu-x86_64时报错:
这里其实主要的报错信息是,在连接的时候找不到
libgcc_s
了,查找了项目的目录,确实没有发现该lib,因此我的处理方式是根据指令中指定的库查找路径(通过-L指定),在某一个库查找路径中(这里我选择的是OH/prebuilts/clang/ohos/linux-x86_64/llvm/lib/x86_64-linux-ohos
)创建一个libgcc_s.so符号链接,指向我本机的gcc中提供的libgcc_s
,这样就可以解决这个问题了。但是需要注意的是,由于我的libgcc_s位于需要root权限才能访问的目录下,所以这样处理之后我都需要使用sudo来执行build脚本了。
在后面还有遇到一些关于库的问题,比如在
libgcc_s.so
库中调用了libgcc_s.so.1
库,而libgcc_s.so.1
在OH目录下也没有,因此可以使用同样的方法进行处理(只能说还好这些库应该是向前兼容的)。我的
libgcc_s.so.1
库位于路径/usr/lib/x86_64-linux-gnu/
中,使用find指令竟然没有找到。。。- 在编译的过程中出现一个警告:
这个报错好处理,直接使用下面的指令安装
gcc-arm-linux-gnueabi
即可:- 编译OHv4、qemu-x86_64时报错:
这个报错也比较简单,主要是因为
OHOS::HiviewDFX::DfxRegsX86_64::GetFramePointerMiniRegs
没有被定义。因此我们只需要进到对应的文件中,为该函数增加定义即可。在这里我参考了官方仓库更新版本的OH(4.1)中的定义方式:做出的更改如下:
也就是增加了一个空的定义。
- 编译OHv4、qemu-x86_64时报错:
这个报错也简单,主要是类型不对,这里只需要去在传参的时候做一下强制类型转换即可:
后面还出现了很多类型不对的报错,只需要去对应的文件中做强制类型转换即可,这里就不一一贴出来了。
这里还有一个比较有代表性的报错:
这里我还是参考了官方仓库中更新版本的代码(OHv5):
在这里需要对domin的类型进行修改:
在后面有很多成员变量的类型都要从
c_char
修改为u8
,原因是c_char
在Rust中实际对应的类型应该是i8
- 在编译OHv4 arm-virt时报错:找不到
vendor.img
参考下面的issue即可解决:
由于我使用的是OHv4版本,因此我们需要在
vendor/ohemu/qemu_arm_linux_min/config.json
文件中增加下面的子系统:- 在运行镜像的时候出现Permission deny:
而当加上sudo之后又出现:
这是因为我的qemu6.2是安装在了当前用户的目录下,而使用sudo时,将使用另一套环境变量。
sudo使用的环境变量与root用户的环境变量都有可能不是一致的。因此在解决这个问题的过程中,我最开始尝试的是配置root用户的环境变量,配置之后就会出现root用户可以使用qemu-system-arm,但是在使用sudo的时候仍然找不到qemu-system-arm。
可以使用下面的指令查看执行sudo的时候使用的环境变量是什么:
在输出的信息中可以发现,sudo的PATH环境变量被设置为了:
因此我这里的处理是,切换至root用户,并在在/etc/sudoers.d目录下新建一个文件来修改sudo使用的环境变量。文件内容如下:
- 在打补丁是出现Hunk报错
- 尽量修改补丁文件以匹配现有的OH Linux。
- 同时修改补丁和OH Linux。也就是在OH Linux中增加补丁需要删除的代码,但是对这些代码加上注释,以保证没有打RT补丁时能正常编译运行。同时也在补丁文件中为需要删除的部分增加注释的反斜杠
- 如果最终还是没有办法打上补丁的话,就只能在OH Linux中增加补丁需要删除的代码(这样可能会导致没有打上RT patch的内核无法运行!!!)
- 有一些内核源码因为我们的设置而不会被编译,这些代码应该就可以随便改改了)
参考:
出现这个问题主要是因为打补丁的时候,有一些补丁没有办法匹配上。因此只需要打开OH中的Linux源文件,然后查看补丁是否匹配即可。如果不匹配的话,就先尝试进行一些等价的变换。如果还是不行的话就需要尝试参考官方Linux进行一定的修改,才能正确打上补丁。
如果想要快速找到那些补丁没有被打上的话,还可以查看构建内核的目录下生成的.rej文件,这些文件保存了被reject的补丁操作。
我这里采取一个统一的策略:
需要注意的是,打补丁进行匹配的时候,还会匹配一些不可见的字符,比如空格和tab键。
- 在打RT补丁时报错:
出现这个问题主要是因为我修改了补丁文件,导致某个补丁头中规定的行数不匹配了。补丁头中会规定原文件从某一行开始的多少行内容,以及新文件从某一行开始的多少行内容。如:
上面的补丁头部分就是表示,修改的位置为原文件的176行起的6行内容,为新文件176行起的15行内容。在打补丁的时候,从原文件(新文件)的哪一行起貌似没有那么重要。因为在打补丁的时候可能会为起始行数增加偏置值。而补丁的范围是不能通过这种方式解决的。补丁的范围就好像编程语言中的数组大小一样,规定了打补丁的范围。上面的问题就是由于补丁的范围错误导致的报错。
需要注意的是,计算原文件(新文件)中的补丁范围时,需要算上补丁开始和结尾的定位行。
疑问
- KERNEL_BUILD_ROOT的两个值?在shell脚本中创建符号链接部分使用的相对路径是什么鬼?
- 看一下patch.sh脚本中的符号链接
- gn是如何使用
config.json
文件的?
- 作者:Noah
- 链接:https://imnoah.top/article/OH_Dev/Task1
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。