SerenityOS 与 Ladybird 的 GN 构建系统指南:从环境搭建到目标构建与运行
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
本指南以仓库中 Meta/gn/README.md 为骨架,系统讲解 SerenityOS 项目中实验性的 GN 元构建系统:如何安装 GN、生成 ninja 构建目录、编写args.gn、按目录前缀构建 Ladybird 与 LibWeb 等目标,以及定位二进制产物并运行。GN 构建并非官方构建方式,官方方案是 CMake,因此本文同时结合仓库内的 GN 源码文件(如 Meta/gn/secondary/BUILD.gn、Toolchain/BuildGN.sh)逐层解释其实现细节,帮助你判断何时该用 GN、出了问题如何自愈。
一、认识 GN:元构建系统与它在 SerenityOS 中的定位
GN(Generate Ninja)是一种元构建系统:它本身不直接编译代码,而是生成 ninja 构建文件,再由 ninja 执行实际编译。除 ninja 之外,GN 还能生成部分 IDE 工程(MSVC、Xcode 等),这些 IDE 工程在构建时依然会调用 ninja。
在 SerenityOS 仓库中,GN 构建处于**实验性(experimental)且尽力而为(best-effort)**的状态。原文档明确警告:GN 构建可能无法正常工作,如果你使用它,需要具备自行修复的能力;SerenityOS 的官方构建系统是 CMake,拿不准时请使用 CMake。仓库还定下了一条协作约定:作者新增文件时只需更新 CMake 构建,不必更新 GN 构建文件,评审者也不应要求作者维护 GN 构建——GN 构建文件的同步维护责任在使用它的人身上。这一约定说明 GN 构建是面向内部开发者与特定使用者的可选通道,而不是面向所有贡献者的强制路径。
GN 的整体设计理念、动机与灵感,可参考 LLVM 项目中关于其 GN 构建的文档,以及官方提供的 GN 概览(详见原文档中的外部链接,本文不展开外部内容)。
从仓库结构看,GN 构建的根定义位于 Meta/gn/secondary/BUILD.gn,它通过import引入 Meta/gn/build/toolchain/compiler.gni 并定义了若干顶层目标组;而 Meta/gn/build/BUILDCONFIG.gn 则为所有二进制目标设置了默认配置(compiler_defaults、no_exceptions、pic),并规定默认工具链为//Meta/gn/build/toolchain:unix。理解这些根文件,有助于在 GN 报错时定位问题。
二、安装 GN:从源码构建与获取预编译二进制
使用 GN 构建前必须先安装 GN。部分 Linux 发行版的软件包仓库提供 GN,但版本可能过旧——原文档以 Ubuntu 22.04 为例指出,其主仓库中的 GN 版本不够新,因此需要自行从源码构建或从 Google 获取二进制。
从源码构建(推荐路径):仓库提供了现成的构建脚本 Toolchain/BuildGN.sh。脚本的核心逻辑如下:
- 将 GN 源码克隆到
Toolchain/Tarballs/gn,并固定 checkout 到提交fae280eabe5d31accc53100137459ece19a7a295,保证可复现; - 调用
./build/gen.py --out-path="$DIR/Build/gn" --allow-warnings生成构建文件; - 通过
ninja -C "$DIR/Build/gn"编译 GN; - 将产物复制到
Toolchain/Local/gn/bin目录。
也就是说,运行该脚本后,GN 二进制位于Toolchain/Local/gn/bin。注意脚本开头会检查是否以 root 运行(exit_if_running_as_root),因为以 root 运行会导致 Toolchain 目录部分文件归属 root,影响后续使用。
获取预编译二进制:也可以按照 GN 官方文档(googlesource 上的 "Getting a binary" 小节)从 Google 下载预编译版本,适用于不方便自行编译的场合。
三、生成构建目录:gn gen 与 ninja 的配合
安装好 GN 后,进入仓库根目录执行:
gn gen outgn gen out会在out目录中生成一套 ninja 构建文件。之后即可用 ninja 构建整个项目:
ninja -C out如果 GN 或 ninja 报告大量错误,很可能是缺少args.gn配置——即没有为构建指定所需工具链。args.gn位于构建目录根(即out/args.gn),有两种处理方式:
- 在运行
gn gen之前手动把args.gn放到构建目录中; - 运行
gn args <build dir>交互式编辑(例如gn args out)。
需要特别注意的是:如果你在gn args之外手动修改了args.gn,必须重新运行gn gen以重新生成 ninja 文件,否则修改不会生效。
3.1 默认配置与工具链兜底
从 Meta/gn/build/BUILDCONFIG.gn 可以看到,仓库对executable、shared_library、static_library、source_set、loadable_module等目标类型统一注入了//Meta/gn/build:compiler_defaults、//Meta/gn/build:no_exceptions与//Meta/gn/build:pic三组默认 config,并支持目标在本地configs中移除。target_os、target_cpu等未显式设置时会回退到宿主(host)值,host_toolchain固定为//Meta/gn/build/toolchain:unix。这意味着在 macOS 上通常可以直接使用默认参数构建。
四、编写 args.gn:典型配置详解
args.gn是 GN 构建的核心配置文件,语法为name = value,注释以#开头。原文档给出了两个典型场景。
macOS:默认参数即可开箱即用。若通过 Homebrew 安装了 Qt6 且具备 Xcode 工具链,编译 Ladybird 通常无需额外定制。
Ubuntu(及多数 Linux):默认的cc/c++很可能无法编译本项目,编译 Ladybird 时一份典型args.gn如下:
# Set build arguments here. See `gn help buildargs`. # Chosen clang must be >= version 15.0.0 host_cc="clang" host_cxx="clang++" is_clang=true use_lld=true qt_install_headers="/usr/include/x86_64-linux-gnu/qt6/" qt_install_lib="/usr/lib/x86_64-linux-gnu" qt_install_libexec="/usr/lib/qt6/libexec/"各参数含义:
| 参数 | 含义 | 备注 |
|---|---|---|
host_cc/host_cxx | 宿主编译器的 C/C++ 可执行文件 | 必须选择 clang,且版本≥ 15.0.0 |
is_clang | 是否使用 clang 工具链 | 置为true使 GN 使用 clang 相关的 flag 集合 |
use_lld | 是否使用 lld 链接器 | 置为true可显著提升链接速度与兼容性 |
qt_install_headers | Qt6 头文件安装目录 | 指向发行版 Qt6 头文件位置 |
qt_install_lib | Qt6 库文件安装目录 | 用于链接 Qt6 库 |
qt_install_libexec | Qt6 辅助程序(moc/rcc)所在目录 | 构建时调用 moc、rcc 等工具 |
这些 Qt 参数在源码中有对应的默认推导逻辑。查看 Meta/gn/secondary/Ladybird/qt_install_prefix.gni:当未显式覆盖时,qt_install_prefix在 macOS 上默认取/opt/homebrew/(Homebrew 的 Apple Silicon 前缀),在 Linux 上默认取/usr/;qt_install_headers默认等于qt_install_prefix + "include/",qt_install_lib默认等于qt_install_prefix + "lib",qt_install_libexec默认等于qt_install_prefix + "share/qt/libexec/"(macOS 上另有qt_install_frameworks指向qt_install_prefix + "Frameworks/")。因此上面的 Linux 显式配置本质是在发行版实际目录与默认前缀不一致时做的覆盖。
这些变量被以下 gni 模板实际消费:
- Meta/gn/secondary/Ladybird/moc_qt_objects.gni:调用
qt_install_libexec + "moc"对 Qt 头文件执行元对象编译器; - Meta/gn/secondary/Ladybird/compile_qt_resource_file.gni:调用
qt_install_libexec + "rcc"编译ladybird.qrc资源文件; - Meta/gn/secondary/Ladybird/link_qt.gni:以
qt_install_headers与qt_install_lib设置 include 与 lib 搜索路径。
另一个相关参数是enable_qt,定义于 Meta/gn/secondary/Ladybird/enable_qt.gni:默认enable_qt = current_os != "mac",即非 macOS 平台默认启用 Qt 界面(macOS 使用 AppKit 界面)。
调试构建参数的通用方法:与任何 GN 工程一样,gn args <build dir> --list是你最好的朋友——它会列出当前构建目录中全部可用参数及其默认值,用于确认哪些参数生效、哪些需要覆盖。
五、按目标构建:目录前缀命名规则
GN 中目标名以声明所在目录为前缀。原文档给出的两个示例:
ninja -C out Ladybird ninja -C out Userland/Libraries/LibWebninja -C out Ladybird构建 Meta/gn/secondary/Ladybird/BUILD.gn 中声明的Ladybird目标组;ninja -C out Userland/Libraries/LibWeb构建 LibWeb 库目标。
查看 Meta/gn/secondary/Ladybird/BUILD.gn 可以看到该目标组的实现细节:
- 在 macOS 上
Ladybird组依赖Ladybird.app(通过create_bundle生成 .app 应用包),其他平台则依赖ladybird_executable可执行文件; - Ladybird 的 Qt 界面由
moc_qt_objects(对 13 个 Qt 头文件执行 moc)、compile_qt_resource_file(编译Ladybird/Qt/ladybird.qrc)与link_qt(链接 Core、Gui、Widgets、Network 四个 Qt 组件)三个模板支撑; - 通过
data_deps拉取 5 个辅助进程目标:ImageDecoder、RequestServer、SQLServer、WebContent、WebWorker(它们各自位于 Meta/gn/secondary/Ladybird/ 下的独立 BUILD.gn); - 非 macOS 平台上还包含
headless-browser无头浏览器目标,以及一系列copy目标,把 emoji、字体、图标、主题、Web 资源与模板(来自Base/res/目录)复制到out/share/Lagom/下的对应子目录,供运行期加载。
此外,根目标组 Meta/gn/secondary/BUILD.gn 还提供了几个预置入口:
default(默认目标):聚合//Ladybird、//Meta/Lagom/Tools/CodeGenerators/IPCCompiler、//Tests、//Userland/Libraries/LibWeb、//Userland/Utilities:base64、//Userland/Utilities:js,标记为testonly;serenity:通过//Kernel(//Meta/gn/build/toolchain:serenity)指定 serenity 工具链来构建内核;macpdf:仅在 macOS 上构建 Meta/Lagom/Contrib/MacPDF;consolepool:利用 ninja 内置的 console pool(要求 GN 版本 ≥ 552353),将某些任务串行化。
六、SerenityOS 内核目标的 GN 支持(未完成)
GN 构建对 SerenityOS 本身的支持仍不完整,目前只能实验性地构建内核,命令为:
ninja -C out serenity对应实现即 Meta/gn/secondary/BUILD.gn 中的serenity目标组,它唯一依赖//Kernel(//Meta/gn/build/toolchain:serenity)——即使用名为serenity的工具链定义来编译 Meta/gn/secondary/Kernel/BUILD.gn 中的内核目标。从仓库结构看,Kernel 的 GN 构建还包含 Prekernel(Meta/gn/secondary/Kernel/Prekernel/BUILD.gn)、generate_version_header.py与post_process_kernel.py等辅助脚本,说明其仍处于搭建阶段,切勿将其视为完整可用的内核构建通道。
七、运行 GN 构建产物
所有可执行文件被统一放到out/bin目录。构建完成后可直接运行:
./out/bin/Ladybird在 macOS 上,产物是.app应用包,可用open启动,同时把标准输出/错误转发到当前终端:
open -W --stdout $(tty) --stderr $(tty) ./out/bin/Ladybird.app --args https://ladybird.dev其中--stdout $(tty) --stderr $(tty)用于把应用日志重定向回终端便于调试,--args之后可以跟启动参数(示例中传入了一个 URL)。
需要注意,Ladybird 这类多进程程序在运行期还需要定位资源文件。根据 Meta/gn/secondary/Ladybird/BUILD.gn 的 copy 目标,非 macOS 平台上 emoji、字体、图标、主题与 Web 资源会被复制到$root_out_dir/share/Lagom/下(如share/Lagom/fonts/、share/Lagom/emoji/、share/Lagom/ladybird/等);macOS 上则通过bundle_data打入.app包的Contents/Resources中。若运行二进制时出现找不到资源的问题,可优先检查这些路径是否已正确生成。
八、测试目标的 GN 入口
与 GN 构建配套的测试聚合定义在 Meta/gn/secondary/Tests/BUILD.gn:Tests目标组聚合了//Tests/AK、//Tests/LibGfx、//Tests/LibJS、//Tests/LibURL、//Tests/LibWeb五个子测试目标,并标记为testonly。构建测试可用:
ninja -C out Tests九、故障排查与维护建议
综合原文档与仓库实现,使用 GN 构建时建议遵循以下排查顺序:
- 确认 GN 版本:至少满足仓库要求(console pool 需要 GN ≥ 552353 对应的版本;Ubuntu 22.04 仓库自带的 GN 版本过旧,务必使用 Toolchain/BuildGN.sh 构建或下载预编译版);
- 确认工具链参数:Linux 上确保
args.gn指定了 clang(≥ 15.0.0)与use_lld=true,且 Qt 相关路径与发行版实际安装位置一致;不确定时用gn args out --list核对所有参数默认值; - 修改 args.gn 后重新生成:手动编辑过
args.gn必须重跑gn gen out,否则 ninja 文件与参数不同步; - 按目录前缀定位目标:想构建哪个目录的功能,就用
ninja -C out <目录路径>的形式指定目标,不要凭猜测使用目标名; - 认清实验性质:GN 构建可能随时损坏且不属于评审维护范围,遇到问题时按“使用者自行修复”的预期处理,核心开发仍以 CMake 为准。
总而言之,GN 构建为 SerenityOS 与 Ladybird 提供了一条实验性的快速构建通道,尤其适合不依赖完整 CMake 工程而希望按目录粒度增量构建 Ladybird、LibWeb 与测试的开发者。掌握gn gen、args.gn参数覆盖与目录前缀目标命名这三大要点,即可在官方 CMake 之外多一条可用的构建路径。
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考