請教現在的工程是如何生成 xr_system.img 的?
-
最近看了幾天的編譯構建,目前還是沒有搞清楚 xr_system.img 是如何編譯出來的?希望有懂的人可以指點一下。
-
最近幾天學習了構建的過程,再次記錄描述一下。
該文件記錄 XR806 工程的編譯過程
涉及到的組件
* [GN](https://gn.googlesource.com/gn/+/main/docs/reference.md) * [ninja](https://ninja-build.org/)
編譯命令
階段一: 配置以及編譯扳機的原生靜態庫
cd device/xradio/xr806/xr_skylark
切換到處理器層級目錄cp project/demo/audio_demo/gcc/defconfig .config
複製 demo 工程的配置文件make menuconfig;make build_clean
根據 demo 配置工程,並進行工程清理, 實際的目標定義在 project/prject.mk 文件中- <a id='SDKconfig'>配置工程</a>,生成 ../liteos_m/SDKconfig.gni 文件,
這一步很重要
特別地在 python config.py 過程中會根據 .config 文件生成 ../liteos_m/SDKconfig.gni 文件,這個文件在後續構建鴻蒙的時候會用到.menuconfig: mconf $< ./Kconfig python config.py
2.# 在文件 gcc/Makefile 中定義的 ROOT_PATH # 絕對路徑 device/xradio/xr806/xr_skylark 是處理器級別的 ROOT_PATH := ../../../.. build_clean: clean lib_clean lib_install_clean -$(Q)-rm -rf $(ROOT_PATH)/out/* # 刪除編譯的工程文件 clean: $(Q)-rm -f $(PROJECT_LD) $(PROJECT).* *.bin $(OBJS) $(DEPS) $(Q)-rm -rf $(ROOT_PATH)/out/* lib_clean: # 進入到 **src** 目錄執行對應的清除動作 $(Q)$(MAKE) $(S) -C $(ROOT_PATH)/src clean lib_install_clean: $(Q)$(MAKE) $(S) -C $(ROOT_PATH)/src install_clean
- <a id='SDKconfig'>配置工程</a>,生成 ../liteos_m/SDKconfig.gni 文件,
make lib -j
編譯設備層級的 lib 文件,这些 lib 文件在当前处理器级别的 src 目录,并且会将这些 .a 文件复制到当前处理器界别的 lib 目录。lib: __lib # 根據 .config 生成對應的 autoconf.h 頭文件 # 執行的命令是 $(CONF) --syncconfig $(Kconfig) 文件就可以生成 include/generated/autoconf.h 頭文件 include/generated/autoconf.h: $(Q)cd $(ROOT_PATH); $(MAKE) prj=$(PRJ_PARENT_DIR)/$(PROJECT) $@; cd - ifeq ($(sdk_cfg_rdy), y) # 第二次開始編譯庫並執行對應 src 目錄的 Makefile __lib: # 执行 src 目录的 Makefile $(Q)$(MAKE) $(S) -C $(ROOT_PATH)/src install else # 第一次生成頭文件 __lib: include/generated/autoconf.h $(Q)$(MAKE) $(S) $@ sdk_cfg_rdy=y endif install: $(Q)$(MAKE) _install TARGET=install _install: $(SUBDIRS) # 分别去对应当的 SUBDIRS 中执行 install $(SUBDIRS): $(Q)$(MAKE) $(S) -C $@ $(TARGET) # 以 src/console/ 目錄下的 Makefile 爲例 # 编译 lib 的目标就是 install, 該目標定義在 src/lib.mk 文件中 # 如果没有特殊指定,那么 lib 的安装目录文件在處理器級別的 lib/ 目录 INSTALL_PATH ?= $(ROOT_PATH)/lib install: $(LIBS) # 将生成的 .a 库文件移动到指定的目录 $(Q)$(CP) -t $(INSTALL_PATH) $^
階段二: 配置鴻蒙系統
cd -
进入到整个鸿蒙工程的根目录hb set
调用 hb 工具的 set 参数。 hb 是鸿蒙构建写的一个 python 工具集合,这个工具的源码在 XR806 这个鸿蒙的工程中 build/lite/hb 目录。 里面 main.py 文件的 main() 函数是这个工具的入口函数。- 针对 set 参数,会 import hb.set.set module, 执行该模块的 exec_command 函数
- hb.set.set 模块的 exec_command 函数会提示配置 product 目录。配置完成后,会配置 hb 的唯一的 config 对象(Config 类的单例模式). 关键的的内容为(以选择 wifi_skylark@xradio 为例子):
- config.product = wifi_skylark
- config.product_path = vendor/xradio/xr806
- config.board config.kernel config.kernel_version config.dev_company 都是从 config.product 目录的 config.json 文件中获取的参数信息
- config.device_path = device/xradio/xr806/liteos_m
这个路径比较重要!!!
階段三: 編譯鴻蒙系統
hb build -f -c gcc
, 備註,因爲我使用的是 gcc 工具鏈,所以額外添加了參數 -c gcc. 執行的 hb.build.build 的 exec_command 函數. 這個是入口,該函數在文件 build/lite/build/build.py 中定義。特別地,在加載對應 module 的時候,會首先執行對應 module 的 add_options 函數,添加針對這個 module 的參數列表,下面步驟截取關鍵的內容展開描述:- build.register_args('ohos_build_type', args.build_type[0]) # 如果沒有 -b 指定編譯類型,默認是 debug, 設置 build 對象的 ohos_build_type 爲 debug
- build.compiler = args.compiler[0] # 如果通過 -c 選項指定了編譯器類型,那麼會使用新的編譯器類型複製給 build 對象的 compiler 參數
- 如果帶有 -f 選項, args.full = true
- return build.build(args.full, cmd_args=cmd_args) 執行 build 構建過程,在文件 build/build_process.py 文件中定義
- self.check_in_device()
- 使用階段二配置的鴻蒙工程參數,設置 build 對象的 product_path,device_path,ohos_kernel_type
- 設置全局的 config 的 out_path 爲鴻蒙工程根目錄/out/${board}/${product} 目錄
- 將 product_path 的 config.json 文件內容相關的參數列表,添加到 build 對象的 args_list 參數列表
- cmd_list = self.get_cmd(full_compile, ninja) -> self.get_cmd(True, True)
- return [self.gn_build, self.ninja_build] 執行 build 對象的 gb_build 和 ninja_build
- gn_build -> gn_path gen out/${board}/${product} --root=鴻蒙的根目錄 --dotfile=鴻蒙的根路徑/build/lite/.gn --script-executable=python3 --args=參數列表
- ninja_build -> ninja_path -w dupbuild=warn -C out/${board}${product} + ninja_args
特別地,因爲在整個編譯過程中不是執行編譯腳本,而是通過 hb.common.utils 工具的 exec_command 函數調用的這些動作,這個函數的一個重要作用是實現了輸出的重定向, 將編譯的過程輸出重定向到了文件中. 不管是 gn_build 還是 ninja_build 都是將這些編譯日志信息重定向到了文件 out/${board}/${product}/build.log 中.
# 這裏對輸出進行了重定向, 講具體的編譯過程重定向到指定的文件中 def exec_command(cmd, log_path='out/build.log', **kwargs): ...
- return [self.gn_build, self.ninja_build] 執行 build 對象的 gb_build 和 ninja_build
- self.check_in_device()
階段四:執行構建生成 img 文件
經過前三個命令後就可以在 product 級別的 out 目錄
device/xradio/xr806/xr_skylark/out/
中找到生成的xr_system.img
文件了,具體這個文件是怎麼生成的呢。這個階段對階段三的 gn 構建部分進行展開闡述。- gn gen out/xradio/xr806 --root=. --dotfile=build/lite/.gn 參數列表
- --root 參數指定了 gn 構建的根目錄
- --dotfile 參數明確指定了構建的 .gn 文件, 默認會在 root 目錄查找 .gn 文件, 特別地 .gn 文件也會定義 root 變量,確定 gn 構建的根目錄. build/lite/.gn 文件內容是:
build/lite/config/BUILDCONFIG.gn 文件內容是:# The location of the build configuration file. # gn build 的 config 文件 buildconfig = "//build/lite/config/BUILDCONFIG.gn" # The source root location. # 重新定義了構建的 root 目錄 root = "//build/lite"
device/xradio/xr806/liteos_m/config.gni 文件內容是,涉及到 SDKconfig.gni 文件生成:import("//build/lite/ohos_var.gni") import("${device_path}/config.gni") -> import("//device/xradio/xr806/liteos_m/config.gni") ...
device/xradio/xr806/liteos_m/SDKconfig.gni 文件內容是:import("//device/xradio/xr806/liteos_m/SDKconfig.gni") -> 這個文件是在階段一配置過程中生成的 ... # Board related headfiles search path. SDKpath = "//device/xradio/xr806" -> 定義了 SDK 的路徑 ... board_include_dirs += [ ... "${SDKpath}/xr_skylark/project/${ProjectPath}", 添加工程路徑用來搜索頭文件 ... ] ...
到此,gn 的 config 過程暫時告一段落, 主要就是配置了一些環境變量、編譯選項等內容。下一步就是真正的構建了.因爲 root_path 是 //build/lite, 構建會去該目錄查找 BUILD.gn 文件.... ProjectPath = "demo/audio_demo" -> 定義了變量 ProjectPath ...
//build/lite/BUILD.gn 文件內容是:
以上內容將需要構建的依賴都包含 deps 中了,最終生成的 xr_system.img 的入口還是 ${device_path}/../ 這個依賴,下面進行詳細闡述。# 定义了 ohos 这个组 group("ohos") { deps = [] # 默認這裏爲空 if (ohos_build_target == "") { # Step 1: Read product configuration profile. # 讀取 product_path 目錄下的 json 配置文件 # product 級別 vendor/xradio/xr806 目錄下的 config.json 文件 product_cfg = read_file("${product_path}/config.json", "json") # kernel 是 product_cfg 文件配置的類型 liteos_m kernel = product_cfg.kernel_type # Step 2: Loop subsystems configured by product. # 遍歷 config.json 文件中定義的所有的 subsystems foreach(product_configed_subsystem, product_cfg.subsystems) { subsystem_name = product_configed_subsystem.subsystem subsystem_info = { } # Step 3: Read OS subsystems profile. # 根據對應的的 subsystem, 讀取 components 對應目錄的 json 配置文件 # subsystem 對應的配置文件保存在 //build/lite/components 目錄, 讀取對應名稱的 json 配置文件 subsystem_info = read_file("//build/lite/components/${subsystem_name}.json", "json") # Step 4: Loop components configured by product. # 遍歷具體的 subsystem 的 components 組件 foreach(product_configed_component, product_configed_subsystem.components) { # Step 5: Check whether the component configured by product is exist. # 查找是否找到了對應的 component, 如果至少找到一個就複製 component_found 爲 true component_found = false foreach(system_component, subsystem_info.components) { if (product_configed_component.component == system_component.component) { component_found = true } } # 如果沒有找到那麼斷言錯誤 assert( component_found, "Component \"${product_configed_component.component}\" not found" + ", please check your product configuration.") # Step 6: Loop OS components and check validity of product configuration. # 如果在對應的 subsystem 的 json 配置文件中找到了對應的定義的 component 組件 # 繼續從頭遍歷該 subsystem foreach(component, subsystem_info.components) { kernel_valid = false board_valid = false # Step 6.1: Skip component which not configured by product. # 如果找到了這個 component if (component.component == product_configed_component.component) { # Step 6.1.1: Loop OS components adapted kernel type. # kernel 根據 product 的 json 配置文件已經初始化爲 liteos_m # 遍歷這個 subsystem 的 component 的所有 adapted_kernel 配置參數 # 如果這個 component 已經適配了指定的 kernel 類型。 表示 component 有效 foreach(component_adapted_kernel, component.adapted_kernel) { # 如果有定義的 kernel 類型,表示匹配,並且該 kernel 類型有效 if (component_adapted_kernel == kernel && kernel_valid == false) { kernel_valid = true } } # 如果該組件沒有適配指定的內核類型,那麼打印錯誤信息 assert( kernel_valid, "Invalid component configed, ${subsystem_name}:${product_configed_component.component} " + "not available for kernel: $kernel!") # Step 6.1.2: Add valid component for compiling. # 將該 component 的 targets 添加到 deps 依賴 foreach(component_target, component.targets) { deps += [ component_target ] } } } } } # Step 7: Add device and product target by default. # 添加設備和 product 的依賴 !!! 這裏是重點 # xr_system.img 文件生成靠的是依賴 ${device_path}/../ 路徑下的 BUILD.gn 文件 deps += [ "${device_path}/../", "${product_path}" ] } else { deps += string_split(ohos_build_target, "&&") } }
/device/xradio/xr806/liteos_m/../BUILD.gn 文件內容是:
上述文件通過... import("//build/lite/config/component/lite_component.gni") -> 定義了 build_ext_command 模板類 ... build_ext_component("libSDK") { exec_path = rebase_path(".", root_build_dir) -> 將當前目錄設置爲相對內置變量 root_build_dir 的相對路徑 outdir = rebase_path("$root_out_dir") -> 將 root_out_dir 設置爲相對系統絕對路徑的路徑格式,轉換到 ourdir 目錄 # 构建的命令, 模板類展開後會通過這個 build.sh 腳本完成 img 的構建 command = "./build.sh ${outdir}" deps = [ "//build/lite/:ohos", "//kernel/liteos_m:kernel", "os:liteos_glue", ] if (IsBootloader == "false") { deps += [ "adapter/hals:adapter", "adapter/console:app_console", "ohosdemo:ohosdemo" -> 這個是我測試自己添加的一個組件 ] } }
build_ext_component
引用了一個模板類,具體的定義在文件 //build/lite/config/component/lite_component.gni, 內容爲:
通過 ./build.sh 最終就可以生成 xr_system.img 文件了。最終的實現在 project/project.mk 文件中定義, 整個 Makefile 的入口是在 device/xradio/xr806/xr_skylark/Makefile。在此僅僅闡述最後生成 img 執行的動作,截取自文件 device/xradio/xr806/xr_skylark/project/project.mk。內容爲:# 定义了一个模板类 template("build_ext_component") { if (defined(invoker.version)) { print(invoker.version) } # 定義了同名的 action action(target_name) { ... -> 在這裏會調用到定義的 command 對象,執行 ./build.sh /系統絕對路徑/out/xr806/wifi_skylark } ... }
cd $(IMAGE_PATH) && \ chmod a+r *.bin && \ $(Q)$(CC) -E -P -CC $(CC_SYMBOLS) -I$(ROOT_PATH)/../include/generated -include autoconf.h -o $(PROJECT_IMG_CFG) - < $(IMAGE_CFG) && \ $(SIGNPACK_GEN_CERT) && \ chmod 777 $(IMAGE_TOOL) && $(IMAGE_TOOL) $(IMAGE_TOOL_OPT) -c $(PROJECT_IMG_CFG) -o $(IMAGE_NAME).img # 講 project 中的 bin 文件複製到頂層目錄下的 out 目錄中 @test -d "$(ROOT_PATH)/out" || mkdir -p "$(ROOT_PATH)/out" $(Q)$(CP) -t $(ROOT_PATH)/out/ $(IMAGE_PATH)/*.bin $(IMAGE_PATH)/$(IMAGE_NAME).img *.map
-
非常感谢分享,很详细
(不过master仓库的打包方式好像变了
(但是思路没变 很有参考意义
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号