构建
unixnu 是构建驱动。它读取设备仓库、解析模块、组装构建树,并按步骤执行。
快速开始
git clone https://github.com/UniXNU/UniXNU
git clone https://github.com/UniXNU/UniXNU-sheng
git clone https://github.com/UniXNU/uefi-sheng
cd UniXNU
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng
./unixnu fetch
./unixnu ipsw fetch
./unixnu build kernel
./unixnu build package
./unixnu build firmware --uefi ../uefi-sheng
./unixnu bootimg --uefi ../uefi-sheng --dtb /path/to/device.dtb第一次使用 -D 后,设备仓库会记录在 out/state.json 中。
启动镜像位于 out/<board>/boot/boot.img,旁边的 boot.json 是报告。选项写在子命令之后。
命令
| 命令 | 作用 |
|---|---|
defconfig [NAME] | 列出设备仓库的 configs/*_defconfig,或把选中的文件复制到 out/<board>/.config。 |
fetch [NAME...] [--update] | 拉取 git 源码并把 commit 写入设备仓库的 unixnu.lock。--update 忽略锁文件。 |
tree | 解析模块、拉取步骤需要的参考源码,并组装 out/<board>/tree。 |
ipsw info | 打印目标 IPSW 的产品、build 和全部组件。 |
ipsw fetch [COMPONENT...] | 把指定组件解压到 out/ipsw/<build>/。 |
build [kernel] [package] [firmware] [payload] [prelink] | 执行指定步骤。默认执行四个步骤。prelink 只重做预链接。 |
build userdata [--dry-run] | 构建 userdata 原始镜像。该步骤较大,只在显式指定时执行。 |
bootimg | 把 BootShim、固件卷、设备树和可选负载打包为 Android 启动镜像。 |
status | 打印设备、配置和每个模块的来源。 |
clean | 删除板级输出,保留已拉取源码。 |
常用选项
| 选项 | 含义 |
|---|---|
-D DIR | 设备仓库,首次使用后会记住。 |
--out DIR | 输出根目录,默认是本仓库的 out/。 |
-j N | 并行任务数,默认取 JOBS 或 CPU 数量。 |
--target DEBUG|RELEASE | EDK II 构建目标,默认 DEBUG。 |
--uefi DIR | 设备的 Mu-Silicium 仓库。 |
--edk2 DIR | Mu_Basecore 仓库。 |
--kernelcache FILE | 原始解压 kernelcache。 |
--kernel FILE | 负载使用的预链接内核。 |
--kernel-arg NAME[=VALUE] | 追加内核参数,可重复。 |
--trust-cache FILE | trust cache 包。 |
--data-ramdisk FILE | Data ramdisk 种子。 |
--skip-setup | 在 Data 种子副本中把 Setup 标记为完成,需要 --data-ramdisk。 |
--unique-chip-id HEX | 设备树平台种子。 |
--ipsw PATH_OR_URL | 目标 IPSW,本地或远程。 |
--skip-preflight | 跳过固件步骤的板级预检脚本。 |
--dry-run | 仅 userdata:检查大小和哈希,不写文件。 |
环境变量 UNIXNU_DEVICE、UNIXNU_OUT、UNIXNU_UEFI、UNIXNU_EDK2、UNIXNU_KERNELCACHE、UNIXNU_DTB、UNIXNU_IPSW、UNIXNU_UNIQUE_CHIP_ID 和 JOBS 提供相同取值。
步骤
defconfig
defconfig 把 configs/<name>_defconfig 复制到 out/<board>/.config,并在构建开始前解析文件,拒绝格式错误的行。
fetch
fetch 解析模块并拉取 git 源码。它使用浅克隆、blobless 过滤和 cone sparse checkout,每个仓库与 ref 一个克隆。每个源码的 commit 写入设备仓库的 unixnu.lock,后续构建使用锁定的 commit。
tree
tree 按脚本期望的布局组装 out/<board>/tree:
tree/
drivers/ kits/ patches/ refer/
scripts/ package/
build/ .cache/模块是指向源码的符号链接,脚本会复制。设备 package/ 出现在 kits/<board.kit>。
ipsw
IPSW 按成员读取,远程 IPSW 不会整体下载。zip 目录和需要的成员通过 HTTP range 请求读取。解压的成员缓存在 out/ipsw/<build>/,并按 board.toml [ipsw.sha256] 的哈希校验。
镜像必须匹配 [ipsw] 的产品、版本、build、device class、chip ID 和 board ID。
kernel
内核步骤:
- 把
patches/xnu和kits/NativePrelink/patches应用到固定版本的 XNU 源码。 - 运行
prepare-xnu-source.py恢复缺失的 DriverKit 头文件。 - 为
ARM64、机型BCM2837、配置DEVELOPMENT、iOS 17 构建 XNU。 - 用
kits/NativePrelink把原生内核与原始 kernelcache 预链接。
kernelcache 来自 IPSW,除非 --kernelcache 指定文件。KernelCache IM4P 的 LZFSE 负载由 macOS compression_tool 解码,选出 arm64 切片,并按 [kernel.prelink] kernelcache_sha256 校验。
输出为 out/<board>/kernel/kernel.macho。
package
package 步骤用 EDK II 构建 package/,输出到 out/<board>/package/Build/Out,包含 UniXNU.efi、UniXNUDxe.efi、HelloPayload.efi、SampleDriver.efi 和 XnuTest.efi。
firmware
固件步骤从 Mu-Silicium 仓库构建设备固件:
- 运行板级预检脚本。
- 把板级补丁应用到共享 edk2 树,构建后还原。
- 用板级 DSC 和 defines 运行 EDK II
build。
FD_BASE、FD_SIZE、FD_BLOCKS 来自 Mu-Silicium 设备配置。固件卷大小必须匹配 [uefi_fd] size。
payload
负载步骤写一个 FAT 卷,包含:
EFI/UniXNU/unixnu.tomlEFI/UniXNU/Payloads/kernel.machoEFI/UniXNU/Payloads/kernel.macho.adt- 可选的
kernel.macho.trustcache - 可选的
scratch-data.img
内核命令行来自 [payload] kernel_args 和每个 --kernel-arg。卷用 mtools 写入,不挂载。输出为 out/<board>/payload/payload.img 和 payload.json。
--skip-setup 在 --data-ramdisk 提供的 Data 种子副本中把 Setup 标记为完成。
userdata
userdata 步骤构建 userdata 分区的原始镜像,保存 IPSW 中未修改的 OS 和 SystemOS 磁盘镜像,前置 SHNGROOT manifest。磁盘镜像用 hdiutil 只读挂载。输出为 out/<board>/userdata/userdata-root.img 和 userdata-root.json,报告会打印 fastboot flash userdata 命令。
bootimg
启动镜像步骤:
- 按固件基址和大小编译 BootShim。
- 追加固件卷。
- 用 gzip 压缩。
- 应用
[[bootimg.dtb_node]]后追加设备树。 - 用 Mu-Silicium 设备配置中的参数调用
mkbootimg.py。 - 解包镜像并逐部分与输入比较。
ramdisk 优先使用负载输出,也可以用 --ramdisk FILE,或用 --ramdisk none 使用空 ramdisk。
本地模块
模块可以来自本地目录,第一个匹配生效:
--local-module [KIND:]NAME=PATH--local-drivers|kits|patches|scripts|package DIR--local DIR,目录布局与本仓库相同feeds.toml中的local = "DIR",或设备仓库的local.toml- 设备仓库
- 本仓库
feeds.toml中的 git 源码
本地模块以符号链接进入构建树。unixnu status 会标记它们,并报告它们没有锁文件固定。
输出
全部输出位于 out/:
| 路径 | 内容 |
|---|---|
out/state.json | 记住的设备仓库 |
out/feeds/ | 拉取的源码 |
out/ipsw/<build>/ | 解压的 IPSW 成员 |
out/<board>/.config | 选中的 defconfig |
out/<board>/tree/ | 组装后的构建树 |
out/<board>/logs/ | 步骤日志 |
out/<board>/kernel/kernel.macho | 预链接内核 |
out/<board>/package/Build/Out/ | EFI 程序 |
out/<board>/payload/ | 负载镜像与报告 |
out/<board>/boot/ | 启动镜像与报告 |
out/<board>/userdata/ | userdata 镜像与报告 |
可复现性
unixnu.lock 固定每个拉取的源码。固件构建把 SOURCE_DATE_EPOCH 设为 Mu-Silicium 的 commit 时间,EDK II 不会写入构建时间。给定固件卷、设备树和 ramdisk 时启动镜像确定。负载包含随机种子,两次构建不同。