构建系统

XWOS的构建系统

俗话说:工欲善其事,必先利其器。 一切美好的想法都需要合适的开发环境来实践。

概述

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.cfgmakefile 环境变量,之后被 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 ,编译输出的目录;
  • $(XWOS_BRD_DIR)/cfg/autogen.h ,自动生成的头文件,被顶级头文件 xwos/standard.h 包含
  • $(XWOS_WKSPC_DIR)/XWOS.cfg.rs :Rust语言的配置
  • $(XWOS_WKSPC_DIR)/env.rcshell 环境变量脚本,可通过 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_ 的宏定义:

编译设备模块

  • 构建系统会递归扫描 xwcd/ 文件夹以及子文件下的所有 xwmo.mk 文件,生成模块列表, 依次将它们编译成 .a 静态库,输出到 $(XWOS_OBJ_DIR) 目录下。
  • 编译时头文件搜索的起始目录为 $(XWOS_PATH)
  • 每个 xwmo.mk 文件代表了一个模块,构建系统会另启一个子进程 make 对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。
  • 每个中间件模块在 $(XWOS_BRD_DIR)/cfg/xwcd.h 中都有一个编译开关 XWCDCFG_ 的宏定义:

编译电路板模块

  • 构建系统会递归扫描 $(XWOS_BM_DIR) 文件夹以及子文件下的所有 xwmo.mk 文件,生成模块列表, 依次将它们编译成 .a 静态库,输出到 $(XWOS_OBJ_DIR) 目录下。
  • 编译时头文件搜索的起始目录为 $(XWOS_PATH)
  • 每个 xwmo.mk 文件代表了一个模块,构建系统会另启一个子进程 make 对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。
  • 每个中间件模块在 $(XWOS_BRD_DIR)/cfg/board.h 中都有一个编译开关 BMCFG_ 的宏定义:

编译第三方软件模块

  • 构建系统会递归扫描 xwem/ 文件夹以及子文件下的所有 xwmo.mk 文件,生成模块列表, 依次将它们编译成 .a 静态库,输出到 $(XWOS_OBJ_DIR) 目录下。
  • 编译时头文件搜索的起始目录为 $(XWOS_PATH)
  • 每个 xwmo.mk 文件代表了一个模块,构建系统会另启一个子进程 make 对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。
  • 每个中间件模块在 $(XWOS_BRD_DIR)/cfg/xwem.h 中都有一个编译开关 XWEMCFG_ 的宏定义:

编译应用模块

  • 构建系统会递归扫描 xwam/ 文件夹以及子文件下的所有 xwmo.mk 文件,生成模块列表, 依次将它们编译成 .a 静态库,输出到 $(XWOS_OBJ_DIR) 目录下。
  • 编译时头文件搜索的起始目录为 $(XWOS_PATH)
  • 每个 xwmo.mk 文件代表了一个模块,构建系统会另启一个子进程 make 对其进行编译, 其中定义了的参数:源代码列表、附加编译选项、头文件路径等对每个编译模块的子进程都是相互独立。
  • 每个中间件模块在 $(XWOS_BRD_DIR)/cfg/xwam.h 中都有一个编译开关 XWAMCFG_ 的宏定义:

编译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_ 的宏定义:

链接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 命令生成编译开关的宏标识符。

增加模块

  1. 中间件模块
  • 模块位于 xwmd/ 目录中;
  • 宏编译开关位于文件 $(XWOS_BRD_DIR)/cfg/xwmd.h ,定义为 1 表示编译模块。
  1. 设备驱动模块
  • 模块位于 xwcd/ 目录中;
  • 宏编译开关位于文件 $(XWOS_BRD_DIR)/cfg/xwcd.h ,定义为 1 表示编译模块。
  1. 电路板模块
  • 模块位于 $(XWOS_BRD_DIR)/bm/ 目录中;
  • 宏编译开关位于文件 $(XWOS_BRD_DIR)/cfg/board.h ,定义为 1 表示编译模块。
  1. 第三方软件模块
  • 模块位于 xwem/ 目录中;
  • 宏编译开位于文件 $(XWOS_BRD_DIR)/cfg/xwem.h ,定义为 1 表示编译模块。
  1. 应用模块
  • 模块位于 xwam/ 目录中;
  • 宏编译开关位于文件 $(XWOS_BRD_DIR)/cfg/xwam.h ,定义为 1 表示编译模块。
  1. OEM模块
  • 模块位于OEM文件夹内;
  • 宏编译开关位于文件 $(XWOS_BRD_DIR)/cfg/oem.h ,定义为 1 表示编译模块。

xwmo.mk 语法说明

xwmo.mkmakefile 的一部分,其中语法简化为定义几个特殊的变量即可完成编译。

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_LIBCpicolibc 就报错。

辅助功能

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的目标
    • 用法举例:
xwm # 编译整个工程
xwm c # 清理
xwm d # 侧底清理
  • xwmm
    • 功能:单独编译模块,类似Android的 mm 命令,使用当前路径作为模块的路径。
    • 选项 -B :全部重新编译一次
    • 用法举例:
cd xwem/vm/lua # 进入到vm/lua模块中
xwmm
  • xwmmm
    • 功能:单独编译模块,类似Android的 mmm 命令,需要指定模块的路径。
    • 选项 -B :全部重新编译一次
    • 用法举例:
xwmmm xwem/vm/lua
  • xwcroot

    • 功能:切换到XWOS的根目录,类似Android的 croot 命令。
  • xwcbd

    • 功能:切换到电路板描述层目录。