构建系统
Categories:
5 分钟阅读
俗话说:工欲善其事,必先利其器。 一切美好的想法都需要合适的开发环境来实践。
概述
XWOS构建系统基于 make
编写,特点:
- 支持纯命令行方式编译,可用于持续集成环境中;
- 可跨平台使用,支持linux、Windows、MACOS;
- 支持基于Eclipse的IDE;
- 为了方便集成第三方软件,XWOS参考了Android的构建系统的
Android.mk
设计出xwmo.mk
, 称为 玄武模块 。
构建流程
flowchart TD make("make") --> cfg cfg["生成配置"] --> arch arch["编译arch.a"] --> cpu cpu["编译cpu.a"] --> soc soc["编译soc.a"] --> brd brd["编译brd.a"] --> xwos xwos["编译XWOS内核"] --> xwmd xwmd["编译中间件模块"] --> xwcd xwcd["编译设备模块"] --> bm bm["编译电路板模块"] --> xwem xwem["编译第三方软件模块"] --> xwam xwam["编译应用模块"] --> oem oem["编译OEM模块"] --> elf elf["链接ELF文件"] --> bin bin["生成bin文件"]
开始
XWOS的构建是从 电路板描述层目录 下执行命令 make
开始的,以 $(XWOS_BRD_DIR)
代表此目录。
执行 make
时,可传递参数:
WKSPC=output-dir
:配置输出文件的路径,可为相对于 电路板描述层目录 的路径,也可为绝对路径;XWOS=topdir
:配置XWOS的根路径,可为相对于 电路板描述层目录 的路径,也可为绝对路径。
生成配置
构建系统调用脚本 XWOS/xwbs/util/el/mkcfg.el
处理 $(XWOS_BRD_DIR)/cfg
下的所有配置文件,
将它们转化成三个文件:
$(XWOS_WKSPC_DIR)/XWOS.cfg
,makefile
环境变量,之后被makefile
引入。- 其中包含一些关键的路径信息,不同的SOC通过这些路径找到各自的编译配置:
$(XWOS_PATH)
: XWOS源码的根目录$(XWOS_ARCH_DIR)
:架构相关的源码路径;$(XWOS_CPU_DIR)
:CPU相关的源码路径;$(XWOS_SOC_DIR)
:SOC相关的源码路径;$(XWOS_BRD_DIR)
:电路板相关的源码路径,此路经也是最开始执行make
的地方;$(XWOS_BM_DIR)
:电路板相关的玄武模块路径;$(XWOS_OEM_DIR)
:OEM相关的玄武模块路径,通常为私有代码;$(XWOS_OBJ_DIR)
:$(XWOS_WKSPC_DIR)/obj
,编译输出的目录;
- 其中包含一些关键的路径信息,不同的SOC通过这些路径找到各自的编译配置:
$(XWOS_BRD_DIR)/cfg/autogen.h
,自动生成的头文件,被顶级头文件xwos/standard.h
包含$(XWOS_WKSPC_DIR)/XWOS.cfg.rs
:Rust语言的配置$(XWOS_WKSPC_DIR)/env.rc
, shell 环境变量脚本,可通过source
命令引入这个文件,使得 辅助功能 生效。
编译 arch.a
构建系统会根据编译规则 $(XWOS_ARCH_DIR)/arch.mk
编译架构描述层(Arch Description Layer)的源码。
编译时头文件搜索的起始目录为 $(XWOS_PATH)
。编译后输出静态库 $(XWOS_OBJ_DIR)/$(XWOS_ARCH_DIR)/arch.a
。
编译 cpu.a
构建系统会根据编译规则 $(XWOS_CPU_DIR)/arch.mk
编译CPU描述层(CPU Description Layer)的源码,
编译时头文件搜索的起始目录为 $(XWOS_PATH)
。编译后输出静态库 $(XWOS_OBJ_DIR)/$(XWOS_CPU_DIR)/cpu.a
。
编译 soc.a
构建系统会根据编译规则 $(XWOS_SOC_DIR)/soc.mk
编译SOC描述层(SOC Description Layer)的源码,
编译时头文件搜索的起始目录为 $(XWOS_PATH)
。编译后输出静态库 $(XWOS_OBJ_DIR)/$(XWOS_SOC_DIR)/soc.a
。
编译 brd.a
构建系统会根据编译规则 $(XWOS_BRD_DIR)/brd.mk
编译电路板描述层(Board Description Layer)的源码,
编译时头文件搜索的起始目录为 $(XWOS_PATH)
。编译后输出静态库 $(XWOS_OBJ_DIR)/$(XWOS_BRD_DIR)/brd.a
。
编译XWOS内核
构建系统会根据编译规则 xwos/xwos.mk
编译XWOS内核的源码,
编译时头文件搜索的起始目录为 $(XWOS_PATH)
。编译后输出静态库 $(XWOS_OBJ_DIR)/$(XWOS_XWOS_DIR)/xwos.a
。
编译中间件模块
- 构建系统会递归扫描
xwmd/
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/xwmd.h
中都有一个编译开关XWMDCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
编译设备模块
- 构建系统会递归扫描
xwcd/
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/xwcd.h
中都有一个编译开关XWCDCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
编译电路板模块
- 构建系统会递归扫描
$(XWOS_BM_DIR)
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/board.h
中都有一个编译开关BMCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
编译第三方软件模块
- 构建系统会递归扫描
xwem/
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/xwem.h
中都有一个编译开关XWEMCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
编译应用模块
- 构建系统会递归扫描
xwam/
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/xwam.h
中都有一个编译开关XWAMCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
编译OEM模块
- OEM文件夹路径由位于配置文件
$(XWOS_BRD_DIR)/cfg/XWOS.h
中的 配置XWCFG_OEMPATH
指定:- 可以是相对于
$(XWOS_BRD_DIR)
的相对路径; - 可以是绝对路径;
- 路径可指向XWOS根目录之外。
- 配置工具
xwbs/util/el/mkcfg.el
会根据此配置生成变量$(XWOS_OEM_DIR)
;
- 可以是相对于
- 构建系统会递归扫描
$(XWOS_OEM_DIR)
文件夹以及子文件下的所有xwmo.mk
文件,生成模块列表, 依次将它们编译成.a
静态库,输出到$(XWOS_OBJ_DIR)/oem
目录下。 - 编译时头文件搜索的起始目录为
$(XWOS_PATH)
。 - 每个
xwmo.mk
文件代表了一个模块,构建系统会另启一个子进程make
对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。 - 每个中间件模块在
$(XWOS_BRD_DIR)/cfg/oem.h
中都有一个编译开关OEMCFG_
的宏定义:1
时表示编译这个模块;0
或 未定义 表示排除这个模块。- 编译开关的命名规则参考 模块编译开关的命名规则 。
链接ELF文件
构建系统最终会将以上生成的所有 .a
静态库链接成 XWOS.elf
文件,
连接脚本由位于配置文件 $(XWOS_BRD_DIR)/cfg/XWOS.h
中的 XWCFG_LDSCRIPT
指定。
生成bin文件
构建系统会将 XWOS.elf 文件转换成 .bin 文件以及 .hex 文件。
构建选项
V
- 作用:输出完整的编译过程。
- 取值:
- 1: 开启选项
- 0: 关闭选项(默认值)
- 用法:
make V=1
D
- 作用:优化编译,输出体积较小的二进制,但不利于调试。
- 取值:
- 1: 开启选项(默认值)
- 0: 关闭选项
- 用法:
make D=0
玄武模块
XWOS的设备模块、中间件模块、第三方软件模块、电路板模块、以及OEM模块,
都是使用 xwmo.mk
来描述编译规则的,将它们统一称为 玄武模块(XWMO) 。
每个 xwmo.mk
文件代表了一个 玄武模块 ,其原理类似于Android系统中的 Android.mk
。
构建系统会另启一个进程 make
对其进行编译,
其中源代码列表、附加编译选项、头文件路径等对每个编译玄武模块的子进程都是独立的。
示例, xwam/example/cxx
的 xwmo.mk
:
include $(XWOS_WKSPC_DIR)/XWOS.cfg # 包含环境变量
include xwbs/functions.mk # 包含xwbs定义的Makefile函数
XWMO_CSRCS := mif.c # 指定C源文件
XWMO_CFLAGS := # 指定附加的C编译选项
XWMO_CXXSRCS := task.cxx # 指定C++源文件
XWMO_CXXSRCS += test/vector.cxx # 增加C++源文件
XWMO_CXXSRCS_gcc += test/literal.cxx test/exception.cxx # 增加仅对gcc生效的C++源文件
XWMO_CXXFLAGS := -Wno-unused-value # 指定C++编译选项
XWMO_CXXFLAGS_gcc += -fexceptions # 增加仅对gcc生效的C++编译选项
XWMO_INCDIRS := $(call getXwmoDir) # 指定附加的头文件搜索路径,其中使用函数getXwmoDir获得当前xwmo的路径
include xwbs/xwmo.mk # 引用包含编译规则的Makefile
模块路径命名规则
由于模块路径需要对应于C语言中的一个宏定义作为编译开关, 因此模块路径需要符合C语言标识符的规则,但可以包含几个特殊符号:
- 路径各级目录命名要符合C语言命名规则;
- 路径中每级目录以
/
隔开; - 路径中可包含
.
,但不能出现../
和./
; - 路径中可包含
-
;
模块编译开关的命名规则
模块必须要有一个与路径对应的宏开关,并被配置为 1
,才能被编译:
- 模块路径只需要转换相对路径部分:
- 中间件模块:取
xwmd/
(不含)之后的路径; - 设备驱动模块:取
xwcd/
(不含)之后的路径; - 电路板模块:取
$(XWOS_BRD_DIR)/bm/
(不含)之后的路径; - 第三方软件模块:取
xwem/
(不含)之后的路径; - 应用模块:取
xwam/
(不含)之后的路径; - OEM模块:取
OEM文件夹/
(不含)之后的路径。
- 中间件模块:取
- 路径中的
_
,需要两个_
来表示; - 路径中的
/
被转换成_
; - 路径中的
.
被转换成_
; - 路径中的
-
被转换成_
; - 增加前缀:
- 中间件模块:
XWMDCFG_
- 设备模块:
XWCDCFG_
- 电路板模块:
BMCFG_
- 第三方软件模块:
XWEMCFG_
- 应用模块:
XWAMCFG_
- OEM模块:
OEMCFG_
- 中间件模块:
- 示例:
- 中间件模块:
xwmd/isc/xwpcp
–>XWMDCFG_isc_xwpcp
; - 设备驱动模块:
xwcd/perpheral/ds/i2c/eeprom
–>XWCDCFG_perpheral_ds_i2c_eeprom
; - 电路板模块:
xwbd/WeActMiniStm32H750/bm/stm32_cube
–>BMCFG_stm32__cube
; - 第三方软件模块:
xwem/vm/l__u_a-5.4.6
–>XWEMCFG_vm_l____u__a_5_4_6
; - 应用模块:
xwam/example/cxx
–>XWAMCFG_example_cxx
; - OEM模块:
oem/app
–>OEMCFG_app
;
- 中间件模块:
- 可以借助 辅助功能 中的
xwmc
命令生成编译开关的宏标识符。
增加模块
- 中间件模块
- 模块位于
xwmd/
目录中; - 宏编译开关位于文件
$(XWOS_BRD_DIR)/cfg/xwmd.h
,定义为1
表示编译模块。
- 设备驱动模块
- 模块位于
xwcd/
目录中; - 宏编译开关位于文件
$(XWOS_BRD_DIR)/cfg/xwcd.h
,定义为1
表示编译模块。
- 电路板模块
- 模块位于
$(XWOS_BRD_DIR)/bm/
目录中; - 宏编译开关位于文件
$(XWOS_BRD_DIR)/cfg/board.h
,定义为1
表示编译模块。
- 第三方软件模块
- 模块位于
xwem/
目录中; - 宏编译开位于文件
$(XWOS_BRD_DIR)/cfg/xwem.h
,定义为1
表示编译模块。
- 应用模块
- 模块位于
xwam/
目录中; - 宏编译开关位于文件
$(XWOS_BRD_DIR)/cfg/xwam.h
,定义为1
表示编译模块。
- OEM模块
- 模块位于OEM文件夹内;
- 宏编译开关位于文件
$(XWOS_BRD_DIR)/cfg/oem.h
,定义为1
表示编译模块。
xwmo.mk
语法说明
xwmo.mk
是 makefile
的一部分,其中语法简化为定义几个特殊的变量即可完成编译。
C/C++
include $(XWOS_WKSPC_DIR)/XWOS.cfg # 包含配置文件
include xwbs/functions.mk # 包含xwbs定义的Makefile函数
XWMO_ASRCS := # 汇编源文件
XWMO_ASRCS_gcc := # 只对gcc起作用的汇编源文件
XWMO_ASRCS_llvm := # 只对llvm起作用的汇编源文件
XWMO_AFLAGS := # 汇编编译选项
XWMO_AFLAGS_gcc := # 只对gcc起作用的汇编编译选项
XWMO_AFLAGS_llvm := # 只对llvm起作用的汇编编译选项
XWMO_CSRCS := # C源文件
XWMO_CSRCS_gcc := # 只对gcc起作用的C源文件
XWMO_CSRCS_llvm := # 只对llvm起作用的C源文件
XWMO_CFLAGS := # C编译选项
XWMO_CFLAGS_gcc := # 只对gcc起作用的C编译选项
XWMO_CFLAGS_llvm := # 只对llvm起作用的C编译选项
XWMO_CXXSRCS := # C++源文件
XWMO_CXXSRCS_gcc := # 只对gcc起作用的C++源文件
XWMO_CXXSRCS_llvm := # 只对llvm起作用的C++源文件
XWMO_CXXFLAGS := # C++编译选项
XWMO_CXXFLAGS_gcc := # 只对gcc起作用的C++编译选项
XWMO_CXXFLAGS_llvm := # 只对llvm起作用的C++编译选项
XWMO_INCDIRS := $(call getXwmoDir) # 获取模块路径并增加到头文件搜索路径
XWMO_INCDIRS_gcc := $(call getXwmoDir) # 获取模块路径并增加到gcc头文件搜索路径
XWMO_INCDIRS_llvm := $(call getXwmoDir) # 获取模块路径并增加到llvm头文件搜索路径
XWMO_LUASRCS := # 转换为C语言数组的Lua脚本源文件
include xwbs/xwmo.mk # 引用包含编译规则的Makefile
RUST模块
include $(XWOS_WKSPC_DIR)/XWOS.cfg # 包含配置文件
include xwbs/functions.mk # 包含xwbs定义的Makefile函数
include xwbs/xwmo.rust.mk # 引用包含编译规则的Makefile
预编译模块
include $(XWOS_WKSPC_DIR)/XWOS.cfg # 包含配置文件
include xwbs/functions.mk # 包含xwbs定义的Makefile函数
XWMO_PREBUILT := # 预先编译好的.a文件
include xwbs/xwmo.prebuilt.mk # 引用包含编译规则的Makefile
xwmo.mk
中可用的函数
-
getXwmoDir
- 用法:
$(call getXwmoDir)
- 简介:获取当前 玄武模块 的路径
- 用法:
-
getXwmoName
- 用法:
$(call getXwmoName)
- 简介:获取当前 玄武模块 的名称
- 用法:
-
XwmoWildcard
- 用法:
$(call XwmoWildcard,WILDCARD,DIR)
- 简介:在目录
DIR
中搜索符合通配符WILDCARD
的文件,并输出文件列表。 - 用法举例
- 示例:
XWMO_CSRCS += $(call XwmoWildcard,*.c,picolibc)
- 说明:在
picolibc
搜索所有的*.c
文件,并将返回的文件列表赋值给XWMO_CSRCS
- 示例:
- 用法:
-
XwmoWildcardRecursively
- 用法:
$(call XwmoWildcardRecursively,WILDCARD,DIR)
- 简介:在目录
DIR
以及其子目录中搜索符合通配符WILDCARD
的文件,并输出文件列表。
- 用法:
-
XwmoReqCfg
- 用法:
$(call XwmoReqCfg,CFG,VALUE)
- 简介:测试配置
CFG
是否为VALUE
,如果不是就报错。 - 用法举例
- 示例:
$(call XwmoReqCfg,XWCFG_LIBC,picolibc)
- 说明:如果配置
XWCFG_LIBC
不为picolibc
就报错。
- 示例:
- 用法:
-
XwmoReqNotCfg
- 用法:
$(call XwmoReqNotCfg,CFG,VALUE)
- 简介:测试配置
CFG
是否为VALUE
,如果是就报错。 - 用法举例
- 示例:
$(call XwmoReqCfg,XWCFG_LIBC,picolibc)
- 说明:如果配置
XWCFG_LIBC
为picolibc
就报错。
- 示例:
- 用法:
辅助功能
XWOS的编译系统类似于Android,也定义了一些与编译相关的辅助命令。
初始化环境
source xwbd/WeActMiniStm32H750/env.sh # 以电路板WeActMiniStm32H750为例
命令
xwmc
- 功能:获取模块编译开关的C语言宏标识符。
- 用法举例:
cd xwem/vm/lua # 进入到vm/lua模块中
xwmc
> XWEMCFG_vm_lua # 输出结果
xwmn
- 功能:获取模块的 .a 文件名。
- 用法举例:
cd xwem/vm/lua # 进入到vm/lua模块中
xwmn
> xwem_vm_lua.a # 输出
xwm
- 功能:编译整个XWOS工程,类似Android的
m
命令。 - 用法:
xwm [选项] [目标]
- 选项
-B
:全部重新编译一次 - 目标:make的目标
- 用法举例:
- 功能:编译整个XWOS工程,类似Android的
xwm # 编译整个工程
xwm c # 清理
xwm d # 侧底清理
xwmm
- 功能:单独编译模块,类似Android的
mm
命令,使用当前路径作为模块的路径。 - 选项
-B
:全部重新编译一次 - 用法举例:
- 功能:单独编译模块,类似Android的
cd xwem/vm/lua # 进入到vm/lua模块中
xwmm
xwmmm
- 功能:单独编译模块,类似Android的
mmm
命令,需要指定模块的路径。 - 选项
-B
:全部重新编译一次 - 用法举例:
- 功能:单独编译模块,类似Android的
xwmmm xwem/vm/lua
-
xwcroot
- 功能:切换到XWOS的根目录,类似Android的
croot
命令。
- 功能:切换到XWOS的根目录,类似Android的
-
xwcbd
- 功能:切换到电路板描述层目录。