4. EDK II 构建系统基础:build命令、target.txt、tools_def.txt、Conf目录、编译流程

EDK II 构建系统是 UEFI 固件开发的基石。理解 build 命令、配置文件体系以及编译流程,是掌握 EDK II 框架的第一步。本章将系统性地拆解构建系统的核心组件,帮助您从“如何编译”深入到“编译如何工作”。

4.1 构建系统全景:Conf 目录与核心配置文件

EDK II 的构建行为由 Conf 目录下的三个核心文本文件控制。在首次运行 edksetup.bat(Windows)或 edksetup.sh(Linux)后,系统会自动从 BaseTools/Conf 模板生成这些文件。

配置文件 作用域 核心作用
target.txt 全局构建目标 定义目标平台、工具链、架构、编译目标(DEBUG/RELEASE/NOOPT)
tools_def.txt 工具链定义 声明所有支持的编译器、链接器、汇编器路径及标志
build_rule.txt 文件类型到动作的映射 定义如何将 .c/.asm/.vfr 等源文件编译为 .obj/.lib/.efi
实践建议: 在团队协作中,建议将 target.txt 和 tools_def.txt 纳入版本管理,但 Conf 目录本身通常不提交,因为它是本地生成环境。

4.2 target.txt:构建目标与策略

target.txt 是构建系统的“总开关”。它定义了构建什么、用什么构建、构建成什么模式。以下是最关键的几个参数:

  • ACTIVE_PLATFORM:指定要构建的 .dsc 文件路径,例如 OvmfPkg/OvmfPkgX64.dsc。
  • TARGET:构建目标,可选 DEBUG、RELEASE、NOOPT。DEBUG 包含调试符号,RELEASE 开启优化,NOOPT 关闭优化但保留符号。
  • TARGET_ARCH:目标架构,如 X64、IA32、AARCH64。支持多架构用空格分隔。
  • TOOL_CHAIN_TAG:工具链标签,如 GCC5、VS2019、CLANG38。必须与 tools_def.txt 中定义的一致。
  • MAX_CONCURRENT_THREAD_NUMBER:并行编译线程数,建议设置为 CPU 核心数,可大幅加速构建。
# target.txt 典型配置片段
ACTIVE_PLATFORM       = OvmfPkg/OvmfPkgX64.dsc
TARGET                = DEBUG
TARGET_ARCH           = X64
TOOL_CHAIN_TAG        = GCC5
MAX_CONCURRENT_THREAD_NUMBER = 8

4.3 tools_def.txt:工具链的“字典”

tools_def.txt 是 EDK II 构建系统的工具链数据库。它采用 FAMILY_TAG_TOOLNAME_FLAGS 的命名约定,为每个工具链定义编译器、链接器、汇编器、归档器的路径和标志。

例如,对于 GCC5 工具链的 X64 架构,其 C 编译器定义如下:

# tools_def.txt 片段
*_GCC5_X64_CC_PATH            = gcc
*_GCC5_X64_CC_FLAGS           = -m64 -g -fshort-wchar -fno-strict-aliasing -Wall -Werror -Wno-array-bounds -c -include AutoGen.h -DSTRING_ARRAY_NAME=$(BASE_NAME)Strings
关键理解: 当您在 target.txt 中设置 TOOL_CHAIN_TAG = GCC5 时,构建系统会自动从 tools_def.txt 中查找所有以 GCC5 开头的定义,组合出完整的编译命令。

4.4 build 命令:构建的入口

build 命令是 EDK II 构建系统的执行入口。它读取 Conf 目录下的配置文件,解析 .dsc、.fdf、.dec 文件,并驱动整个编译流程。

常用命令形式:

# 使用 target.txt 中的默认配置
build

# 指定平台和架构,覆盖 target.txt
build -p OvmfPkg/OvmfPkgX64.dsc -a X64 -t GCC5

# 指定构建目标
build -b RELEASE

# 清理中间文件
build clean

# 清理所有输出(包括最终固件)
build cleanall

# 仅编译指定模块
build -m MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf

4.5 编译流程:Pre-Build → Build → Post-Build

EDK II 的编译流程分为三个阶段,每个阶段都有明确的职责。理解这个流程有助于定位构建失败的原因。

EDK II 编译三阶段流程 阶段一:Pre-Build 生成 AutoGen 文件 解析 .dsc / .dec / .fdf 创建 Build 目录结构 生成 Makefile 输入 阶段二:Build 调用 make/nmake 编译 .c → .obj 链接 .obj → .efi 生成 FV / FD 镜像 输出 阶段三:Post-Build 生成 FV 映射文件 生成 MAP / PDB 符号 执行后处理脚本 生成最终固件文件 关键路径:Pre-Build 生成 AutoGen.h → Build 编译链接 → Post-Build 生成最终固件

4.5.1 Pre-Build:元数据解析与代码生成

Pre-Build 阶段是构建系统的“大脑”。它不编译任何 C 代码,而是执行以下关键任务:

  1. 解析元数据文件:读取 .dsc(平台描述)、.dec(包声明)、.fdf(固件布局)文件,构建完整的依赖树。
  2. 生成 AutoGen 文件:为每个模块生成 AutoGen.h 和 AutoGen.c,包含 GUID、PPI、Protocol 的自动声明。
  3. 生成 Makefile:根据 build_rule.txt 和 tools_def.txt,为每个模块生成独立的 Makefile。
  4. 创建 Build 目录:在 Build/<Platform>/<Target>_<Toolchain> 下创建完整的目录结构。

4.5.2 Build:真正的编译与链接

Build 阶段是构建系统的“肌肉”。它调用底层的 make(Linux)或 nmake(Windows)执行实际的编译工作:

  • 并行编译:利用 MAX_CONCURRENT_THREAD_NUMBER 设置,并行编译所有模块的源文件。
  • 模块链接:将编译生成的 .obj 文件链接为 .efi 可执行文件。
  • 固件卷生成:将多个 .efi 模块打包为 FV(Firmware Volume),并最终生成 FD(Firmware Device)镜像。
常见问题: 如果 Build 阶段失败,请先检查 tools_def.txt 中的工具链路径是否正确,以及 target.txt 中的 TOOL_CHAIN_TAG 是否与已安装的编译器匹配。

4.5.3 Post-Build:收尾与交付

Post-Build 阶段是构建系统的“包装工”。它执行构建后的处理工作:

输出文件 说明
.efi UEFI 可执行文件,可直接在 UEFI Shell 中运行
.fv 固件卷文件,包含多个 .efi 模块的打包镜像
.fd 固件设备文件,最终的 BIOS 镜像文件
.map 符号映射文件,用于调试时地址解析
.pdb Windows 调试符号文件(仅 VS 工具链)

4.6 构建目录结构解析

理解构建输出目录的结构,有助于快速定位编译产物和中间文件。

Build 输出目录结构示例 Build/ OvmfX64/ MdeModule/ 其他平台/ DEBUG_GCC5/ RELEASE_GCC5/ FV/ IA32/ X64/

典型构建输出路径:Build/OvmfX64/DEBUG_GCC5/X64/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe/DEBUG/ConPlatformDxe.efi

4.7 构建流程的自动化与定制

EDK II 支持通过 --pcd 参数在构建时动态修改 PCD(Platform Configuration Database)值,无需修改源文件:

# 在构建时覆盖 PCD 值
build -p OvmfPkg/OvmfPkgX64.dsc -a X64 -t GCC5 --pcd gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel=0x80000000
进阶技巧: 您可以在 target.txt 中添加自定义 BUILD_OPTIONS 行,将常用的 --pcd 参数固化,避免每次手动输入。

通过本章的学习,您应该能够独立配置 target.txt、理解 tools_def.txt 的作用、熟练使用 build 命令,并清晰描述 EDK II 的三阶段编译流程。这是后续深入学习 UEFI 驱动开发、固件定制和调试的基础。