ReactOS 部分编译指南
1. 构建系统概述
ReactOS 使用 CMake + Ninja 构建系统。
- CMake : 生成构建配置文件 (
build.ninja) - Ninja: 执行实际的编译任务(增量构建、并行编译)
构建工具链位于 C:\RosBE\(ReactOS Build Environment),这是一个 MinGW 交叉编译环境。
关键目录
| 路径 | 说明 |
|---|---|
d:\reactos\ |
源代码根目录 |
d:\reactos\output-MinGW-i386\ |
构建输出目录(i386 架构) |
d:\reactos\sdk\cmake\CMakeMacros.cmake |
核心构建宏 |
d:\reactos\toolchain-gcc.cmake |
交叉编译器定义 |
构建配置
d:\reactos\output-MinGW-i386\
├── build.ninja # 完整构建 DAG(约 167,000 行)
├── rules.ninja # 编译/链接规则定义
├── CMakeCache.txt # CMake 配置缓存
├── .ninja_log # 构建时间戳(增量构建用)
└── .ninja_deps # 依赖元数据
2. 基础编译命令
编译所有模块
cmd
cd d:\reactos\output-MinGW-i386
ninja
触发默认的 all 目标,递归构建所有模块。
编译单个模块
cmd
cd d:\reactos\output-MinGW-i386
ninja wininet # 编译 wininet.dll
ninja urlmon # 编译 urlmon.dll
ninja kernel32 # 编译 kernel32.dll
ninja ntoskrnl # 编译内核
ninja win32k # 编译窗口管理器
ninja hal # 编译 HAL
编译安装 CD
cmd
ninja bootcd # 生成安装 CD (bootcd.iso)
ninja livecd # 生成 Live CD (liveimg.iso)
常用 Ninja 参数
cmd
ninja -j 8 # 并行编译(8 个任务)
ninja -j 1 # 单线程编译(避免 I/O 冲突)
ninja -v # 显示完整编译命令
ninja -n # 干跑模式(只显示要做什么,不实际执行)
3. 常用模块目标名
系统 DLL
| 模块 | Ninja 目标 | 产物 |
|---|---|---|
| 网络 | ninja wininet |
dll/win32/wininet/wininet.dll |
| 网络 | ninja urlmon |
dll/win32/urlmon/urlmon.dll |
| Shell | ninja shell32 |
dll/win32/shell32/shell32.dll |
| 用户界面 | ninja user32 |
win32ss/user/user32/user32.dll |
| 内核 | ninja kernel32 |
dll/win32/kernel32/kernel32.dll |
| GDI | ninja gdi32 |
win32ss/gdi/gdi32/gdi32.dll |
| IE 框架 | ninja ieframe |
dll/win32/ieframe/ieframe.dll |
| MSHTML | ninja mshtml |
dll/win32/mshtml/mshtml.dll |
内核与驱动
| 模块 | Ninja 目标 | 产物 |
|---|---|---|
| NT 内核 | ninja ntoskrnl |
ntoskrnl/ntoskrnl.exe |
| 窗口管理 | ninja win32k |
win32ss/win32k.sys |
| HAL | ninja hal |
hal/halx86/hal.dll |
| 文件系统 | ninja fastfat |
drivers/filesystems/fastfat/fastfat.sys |
| TCP/IP | ninja tcpip |
drivers/network/tcpip/tcpip.sys |
其他组件
| 模块 | Ninja 目标 | 产物 |
|---|---|---|
| 安装程序 | ninja usetup |
base/setup/usetup/usetup.exe |
| 设备管理器 | ninja devmgmt |
base/applications/devmgmt/devmgmt.exe |
| 注册表编辑器 | ninja regedit |
base/applications/regedit/regedit.exe |
| 任务管理器 | ninja taskmgr |
base/applications/taskmgr/taskmgr.exe |
4. 增量构建
Ninja 默认支持增量构建。当你修改一个 .c 文件后,Ninja 会自动重建受影响的 .obj 文件和依赖的 .dll。
工作原理
修改 cookie.c
→ Ninja 检测到 cookie.c 比 cookie.c.obj 更新
→ 重新编译 cookie.c.obj
→ 检测到 wininet.dll 依赖的 .obj 有变化
→ 重新链接 wininet.dll
依赖追踪
- 每个
.obj文件有一个对应的.d文件(由 GCC 的-MD标志生成) .d文件记录了该.c文件包含的所有.h头文件- 修改
.h文件会触发所有包含它的.obj重新编译
仅编译修改过的模块
cmd
ninja wininet # 只编译 wininet 及其依赖
ninja urlmon # 只编译 urlmon 及其依赖
你不需要每次都编译 bootcd。Ninja 会自动确保在最终链接时使用最新的 .obj 文件。
5. 清理操作
cmd
# 清理单个模块
ninja -t clean wininet
# 清理所有模块
ninja clean
# 删除 Ninja 状态文件(强制重新检查所有时间戳)
del .ninja_log
del .ninja_deps
6. 手动编译单个文件
当 Ninja 自身无法正常工作时(如 GetOverlappedResult 错误),可以直接使用编译器。
编译单个 .c 文件
从 build.ninja 中提取编译命令。例如编译 cookie.c:
cmd
C:\RosBE\i386\bin\gcc.exe ^
-DDBG=1 -D_DLL -D_WIN32_WINNT=0x600 -D__REACTOS__ ^
-pipe -fms-extensions -fno-strict-aliasing -std=gnu99 ^
-march=pentium -O1 ^
-I ../dll/win32/wininet -I ../sdk/include ^
-I ../sdk/include/crt -I ../sdk/include/psdk ^
-o dll/win32/wininet/CMakeFiles/wininet.dir/cookie.c.obj ^
-c ../dll/win32/wininet/cookie.c
获取编译命令的方法
-
从 build.ninja 文件中查找 :
在
build.ninja中搜索cookie.c.obj找到对应的build规则 -
使用 ninja -t commands:
cmdninja -t commands dll/win32/wininet/CMakeFiles/wininet.dir/cookie.c.obj
编译资源文件 (.rc)
cmd
C:\RosBE\i386\bin\windres.exe -O coff ^
-D__REACTOS__ -I ../dll/win32/wininet ^
-o dll/win32/wininet/CMakeFiles/wininet.dir/rsrc.rc.obj ^
-i ../dll/win32/wininet/rsrc.rc
手动链接 DLL
编译完所有必需的 .obj 文件后,手动链接:
cmd
C:\RosBE\i386\bin\g++.exe ^
-nostdlib -shared ^
-o dll/win32/wininet/wininet.dll ^
dll/win32/wininet/CMakeFiles/wininet.dir/*.obj ^
sdk/lib/3rdparty/libwine/libwine.a ^
sdk/lib/pseh/libpseh.a ^
dll/win32/ws2_32/CMakeFiles/libws2_32.dir/libws2_32.a ^
dll/win32/shlwapi/CMakeFiles/libshlwapi.dir/libshlwapi.a ^
... ^
-lgcc ^
dll/win32/wininet/wininet.def
:: 剥离调试符号
host-tools/bin/rsym.exe -s D:/reactos wininet.dll wininet.dll
7. 常见 Ninja 问题处理
GetOverlappedResult 错误
ninja: fatal: GetOverlappedResult: 操作成功完成。
这是 Ninja 在 Windows 上的已知问题,通常由 I/O 冲突引起。
| 解决方法 | 命令 |
|---|---|
| 直接重试 | ninja |
| 单线程模式 | ninja -j 1 |
| 清理 Ninja 状态 | del .ninja_log && del .ninja_deps && ninja |
| 跳过特定目标 | ninja -t clean wininet && ninja wininet |
| 添加杀毒软件排除 | 将 output-MinGW-i386 目录加入杀毒排除列表 |
build.ninja 需要重新生成
当修改了 CMakeLists.txt 后,需要重新运行 CMake:
cmd
cd output-MinGW-i386
cmake .
模块未找到
cmd
# 列出所有可用的目标
ninja -t targets | findstr wininet
8. bootcd / livecd 与单独编译的关系
┌────────────────────────────────┐
│ bootcd / livecd │
│ (包含所有模块的 ISO 镜像) │
└────────────────────────────────┘
▲
│ 依赖
┌─────────────────────────┐
│ boot/boot_images.cmake │
│ (创建 ISO 镜像) │
└─────────────────────────┘
▲
│ 依赖
┌──────────────────────────────────────┐
│ reactos.cab │
│ (所有用户态 DLL/EXE/驱动 的压缩包) │
└──────────────────────────────────────┘
▲
│ 依赖
┌──────────────────────────────────────────────────┐
│ 各单独模块 (wininet.dll, kernel32.dll, ...) │
└──────────────────────────────────────────────────┘
关键点 : bootcd 是所有模块的超集 。当你运行 ninja bootcd 时:
- Ninja 解析依赖链,找出所有需要更新的模块
- 只重新编译已修改的源文件(增量构建)
- 将所有输出打包成
reactos.cab - 连同引导加载器等一起制作成 ISO
开发工作流:
修改代码 → ninja wininet # 快速编译(秒级)
测试功能 → ninja wininet # 再次快速编译
准备 ISO → ninja bootcd # 编译所有变化 + 打包 ISO
9. RosBE 工具链路径
| 工具 | 路径 | 用途 |
|---|---|---|
| gcc | C:\RosBE\i386\bin\gcc.exe |
C 编译器 |
| g++ | C:\RosBE\i386\bin\g++.exe |
C++ 编译器/链接器 |
| windres | C:\RosBE\i386\bin\windres.exe |
资源文件编译 |
| ar | C:\RosBE\i386\bin\ar.exe |
静态库打包 |
| cmake | C:\RosBE\bin\cmake.exe |
CMake 配置 |
| ninja | C:\RosBE\bin\ninja.exe |
构建执行器 |
主机工具(在构建机器上运行,非交叉编译)
这些工具用宿主编译器编译,位于 output-MinGW-i386/host-tools/bin/:
| 工具 | 用途 |
|---|---|
widl.exe |
处理 .idl 接口定义文件 |
cabman.exe |
创建 reactos.cab 压缩包 |
mkisofs.exe |
制作 ISO 镜像 |
rsym.exe |
剥离 DLL 调试符号 |
spec2def.exe |
将 .spec 文件转换为 .def |
utf16le.exe |
文本编码转换 |
mkhive.exe |
构建注册表配置单元 |
10. 修改 CMakeLists.txt 后的操作
当添加新文件或修改 CMakeLists.txt 后,CMake 会自动检测并重新生成 build.ninja。如果自动检测失败,手动运行:
cmd
cd output-MinGW-i386
cmake .
ninja <目标模块>
添加新源文件到现有模块
编辑模块的 CMakeLists.txt,添加源文件名到 add_library 或 add_executable 的 SOURCES 列表中。
例如 dll/win32/wininet/CMakeLists.txt:
cmake
add_library(wininet MODULE
cookie.c
dialogs.c
ftp.c
http.c # 已有的文件
netconnection.c
new_file.c # 添加这行
...
)
然后运行 cmake . 和 ninja wininet。
11. 在 ReactOS 中测试新编译的 DLL
编译后的 DLL 位于 output-MinGW-i386\dll\win32\<模块名>\<模块名>.dll
方法 1: 直接替换(开发机测试)
cmd
copy output-MinGW-i386\dll\win32\wininet\wininet.dll C:\ReactOS\system32\
方法 2: 打包到 bootcd
cmd
ninja bootcd
生成的 ISO 位于 output-MinGW-i386\bootcd.iso。
方法 3: 单独编译后复制到 ReactOS 虚拟机
cmd
ninja wininet
copy output-MinGW-i386\dll\win32\wininet\wininet.dll \\虚拟机共享目录\
然后在 ReactOS 中替换 %SystemRoot%\system32\wininet.dll。
12. 向 bootcd / livecd 中添加软件/文件的方法
方法一:通过 add_cd_file() 宏(推荐)
add_cd_file() 是 ReactOS 构建系统中最标准的添加文件方式,定义在 sdk/cmake/CMakeMacros.cmake。
语法:
cmake
add_cd_file(
FILE <路径> # 源文件路径
DESTINATION <目录> # 目标目录(如 reactos/system32)
[NO_CAB] # 可选:不压缩到 CAB,直接放 ISO
[NAME_ON_CD <名称>] # 可选:在 CD 上重命名
FOR bootcd|livecd|all # 添加到哪种 CD
)
进 CAB(压缩到 reactos.cab,安装时自动解压)
cmake
# 把 myapp.exe 放到 %SystemRoot%\ 下
add_cd_file(
FILE ${CMAKE_CURRENT_SOURCE_DIR}/myapp.exe
DESTINATION reactos
FOR bootcd
)
# 把 mylib.dll 放到 %SystemRoot%\system32\ 下
add_cd_file(
FILE ${CMAKE_CURRENT_SOURCE_DIR}/mylib.dll
DESTINATION reactos/system32
FOR bootcd
)
注意 : 进 CAB 的文件需要先在 dir_to_num() 和 reactos.dff.in 中添加目标目录映射(详见下文)。
不进 CAB(NO_CAB,直接放在 ISO 上)
cmake
# 直接放在 ISO 的 i386/ 下(bootcd)或 reactos/ 下(livecd)
add_cd_file(
FILE ${CMAKE_CURRENT_SOURCE_DIR}/readme.txt
DESTINATION reactos
NO_CAB
FOR bootcd livecd
)
NO_CAB 模式的特点:
- 文件不会被安装到系统硬盘上,只存在于 ISO 中
- 适合 LiveCD 中使用的文件、说明文档、驱动包等
- bootcd 中
reactos/前缀会被替换为架构名(如i386/) - 不需要 修改
dir_to_num()或reactos.dff.in
方法二:通过 bootcd_extras / livecd_extras 目录(最简单)
这是最快捷的方式------只需把文件放到特定目录下:
d:\reactos\modules\
├── bootcd_extras\ # 放到这里 → ISO 的 extras/ 目录(bootcd)
│ └── readme.txt
└── livecd_extras\ # 放到这里 → ISO 的 extras/ 目录(livecd)
└── my_tool.exe
原理:modules/CMakeLists.txt 中的自动脚本(第 22-37 行)会自动扫描这些目录并调用 add_cd_file(FILE ... NO_CAB ...)。
方法三:通过 reactos.dff.in(手动修改 CAB 列表)
这种方式适合往 reactos.cab 中添加自定义模块。需要同步修改两个地方:
步骤 1:添加目录映射
在 sdk/cmake/CMakeMacros.cmake 的 dir_to_num() 中添加:
cmake
elseif(${dir} STREQUAL reactos/myapp)
set(${var} 90)
步骤 2:添加目录编号
在 boot/bootdata/packages/reactos.dff.in 的 [Directories] 中添加:
ini
90 = myapp
步骤 3:添加文件条目
在 reactos.dff.in 的文件列表区域添加:
ini
"modules/optional/myapp.exe" 90 optional
其中 90 是目录编号,optional 表示文件不存在时跳过(不报错)。
方法四:添加为系统模块(供 setup 安装)
如果要添加的组件在 ReactOS 安装过程中需要被 setup 识别并安装,需要在 boot/bootdata/packages/reactos.dff.in 中添加非 optional 的条目,或者在 dll/、base/ 等目录下创建 CMake 模块并注册。
注册新模块到 bootcd 依赖链
在模块的 CMakeLists.txt 中使用:
cmake
add_cd_file(
TARGET <目标名>
FILE ${CMAKE_CURRENT_BINARY_DIR}/<输出文件>
DESTINATION reactos/<目标目录>
FOR bootcd
)
TARGET 参数会建立 ninja 依赖,确保 bootcd 在生成前先编译该模块。
方法五:添加注册表设置
如果添加的软件需要注册表项,在 boot/bootdata/hivedef.inf 的 [AddReg] 段中添加:
ini
; 示例:添加软件路径到注册表
HKLM,"SOFTWARE\MyCompany\MyApp","InstallPath",,"%SystemRoot%\myapp"
三种方式的对比
| 方式 | 安装时自动解压 | 适合场景 | 复杂度 |
|---|---|---|---|
add_cd_file() + 进 CAB |
是 | 需要被 setup 安装到系统的文件 | 中 |
add_cd_file() + NO_CAB |
否 | LiveCD 工具、说明文档 | 低 |
bootcd_extras/ 目录 |
否 | 快速测试、临时文件 | 最低 |
reactos.dff.in 手动编辑 |
是 | 传统方式、大量文件 | 高 |
注册表 (hivedef.inf) |
--- | 需要注册表配置的组件 | 中 |
典型工作流
cmd
# 1. 把文件放到 bootcd_extras
copy my_readme.txt d:\reactos\modules\bootcd_extras\
# 2. 重新配置 CMake(如果之前没有该目录)
cd d:\reactos\output-MinGW-i386
cmake .
# 3. 重新生成 ISO
ninja bootcd
提示 : 修改
CMakeLists.txt后需要运行cmake .重新生成build.ninja,然后才能用ninja编译。
bootcd_extras/目录不存在时不会报错------脚本用if(EXISTS ...)检测,方便条件性添加。关于 wine-gecko 的完整添加示例,可以参考
modules/CMakeLists.txt末尾的 Wine Gecko 配置块。