多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Nix 源码构建开发指南:从 devShell 到交叉编译的完整实践

Nix 源码构建开发指南:从 devShell 到交叉编译的完整实践 Nix 源码构建开发指南从 devShell 到交叉编译的完整实践【免费下载链接】nixNix, the purely functional package manager项目地址: https://gitcode.com/gh_mirrors/ni/nix本文是 Nix纯函数式包管理器源码仓库的开发Hacking入门指南基于仓库根目录的 HACKING.md 编写。文中完整覆盖两条主流开发路径——经典nix-shell与实验性的nix develop并深入展开支持平台、系统类型System type、交叉编译、多种编译环境stdenv、编辑器 LSP 集成与代码格式化等实践内容结合仓库内的 flake.nix、packaging/dev-shell.nix、meson.build 等源码佐证底层实现帮助你从零搭建 Nix 自身源码的开发、构建、测试与提交环境。前置准备在开始之前请确认环境满足以下条件克隆仓库本指南假设你已经把 Nix 源码克隆到本地。克隆时需注意仓库中包含符号链接symlink因此在 Windows 上克隆前必须启用 git 的core.symlinks设置否则检出会失败。本地已安装 Nix以下所有构建步骤都依赖 Nix 来搭建开发环境下载并注入所有构建依赖、设置环境变量。如果你还没有安装 Nix请先按照安装指引完成安装参见 scripts/install-multi-user.sh 等多用户安装脚本以及 packaging/installer 目录下的安装器实现。经典构建方式nix-shellNix 仓库在根目录提供了 shell.nix通过 default.nix 使用 flake-compat 桥接自 flake 输出因此可以直接用经典 Nix 进入开发环境$ nix-shell进入 shell 后所有构建依赖的环境变量编译器、头文件、库路径等都已就绪。若想使用其他受支持的编译环境见下文“编译环境”一节可以指定对应的 devShell 属性$ nix-shell --attr devShells.x86_64-linux.native-clangStdenv提示使用native-ccacheStdenv可以大幅缩短重复编译的时间。默认情况下ccache 的缓存产物保存在~/.cache/ccache/。在 shell 中构建 Nix在nix-shell中构建 Nix 到本地目录而不是 Nix 存储可以执行以下命令[nix-shell]$ out$(pwd)/outputs/out dev$out debug$out mesonFlags --prefix${out} [nix-shell]$ dontAddPrefix1 configurePhase [nix-shell]$ buildPhase这里通过把prefix指向当前目录下的outputs/out并让 configure/build 阶段直接作用于源码树从而在不污染 Nix 存储的前提下快速迭代。运行测试[nix-shell]$ checkPhase安装到$(pwd)/outputs并验证版本[nix-shell]$ installPhase [nix-shell]$ ./outputs/out/bin/nix --version nix (Nix) 2.12直接构建发布版产物进入 Nix 存储$ nix-build使用 flake 构建nix develop本节假设你的 Nix 已经启用了flakes与nix-command两个实验特性。这两个特性分别对应仓库文档中定义的实验特性开关xp-feature-flakes与xp-feature-nix-command。进入开发环境$ nix develop该 shell 与nix-shell的区别在于它还会把./outputs/bin/nix加入$PATH因此构建完成后可以立刻直接执行nix命令。选择其他编译环境的开发 shell$ nix develop .#native-clangStdenv提示同样地使用ccacheStdenv变体如.#native-ccacheStdenv可大幅改善重复编译时间。构建、测试与安装[nix-shell]$ configurePhase [nix-shell]$ buildPhase [nix-shell]$ checkPhase [nix-shell]$ installPhase [nix-shell]$ nix --version nix (Nix) 2.12注意nix develop路径下无需手动设置out/prefixdevShell 的shellHook已经接管了这些变量见 packaging/dev-shell.nix 中的shellHook定义。构建发布版$ nix build关于如何运行与过滤各类测试单元测试、功能测试、模糊测试等可参考仓库中的测试运行文档doc/manual/source/development/testing.md测试基础设施位于 tests/functional 与各*-tests组件目录。devShell 的底层实现理解nix develop背后发生了什么有助于排查环境问题。从 flake.nix 可以看到devShells由makeShell定义于 packaging/dev-shell.nix针对每个系统与每种 stdenv 生成并统一以native-stdenv名前缀命名默认的native实际指向native-stdenvdevShell 本质是一个stdenv.mkDerivationpname shell-for-nix其shellHook会依次设置PATH、清空PYTHONPATH、导出MANPATH并重定义configurePhase/buildPhase/checkPhase/installPhase使其默认进入build目录后调用 Meson/Ninja 对应的阶段函数所有内部组件nix-util、nix-store、nix-expr等见 meson.build 中的 subproject 列表的buildInputs会通过buildInputsClosureCond做闭包去重后注入 shell避免重复引入内部依赖在 Linux 上CC_LD/CXX_LD被设为mold即默认使用 mold 链接器加速链接此外 shell 中还预装了pre-commit、nixfmt、shellcheck、include-what-you-use、gdbUnix、clang-toolsclang shell等开发工具。支持的平台Nix 可构建的官方平台在 flake.nix 的systems列表与文档中均有列出x86_64-linuxi686-linuxaarch64-linuxaarch64-darwinarmv6l-linuxarmv7l-linuxpowerpc64-linuxELFv1 ABIpowerpc64le-linuxriscv64-linux注意当前 flake.nix 中systems实际只包含i686-linux、x86_64-linux、aarch64-linux与aarch64-darwin其余平台如armv6l-linux、powerpc64-linux等属于通过crossSystems提供的交叉编译目标。为其他平台构建要为与当前机器不同的平台构建 Nix需要先让当前 Nix 能够生成目标平台的代码常见方案有两种远程构建机remote build machines把构建任务分发到目标平台的机器上执行二进制格式模拟binary format emulation仅 NixOS 支持通过 binfmt 在内核层面模拟其他架构的二进制。具备上述条件后只需选择对应属性即可构建。例如为aarch64-linux交叉编译$ nix-build --attr packages.aarch64-linux.default或在启用 flake 的 Nix 中$ nix build .#packages.aarch64-linux.default可用的交叉编译目标文档与 flake.nix 的crossSystems列表共同确认仓库提供了以下交叉编译目标armv6l-linuxarmv7l-linuxpowerpc64-linuxELFv1 ABIpowerpc64le-linuxriscv64-linuxx86_64-freebsdx86_64-w64-mingw32Windows 目标flake 中为其配置了 wine 模拟器如果想要在尚未支持的平台上引导bootstrapNix可以向flake.nix中的crossSystems追加对应的系统类型字符串。在 flake 中这些交叉编译目标以组件 × 目标平台的矩阵形式暴露例如nix build .#nix-everything-riscv64-unknown-linux-gnu nix build .#nix-everything-armv7l-unknown-linux-gnueabihf nix build .#nix-everything-x86_64-unknown-freebsd nix build .#nix-everything-x86_64-w64-mingw32属性名形如组件-triple由 flake.nix 中的flatMapAttrs构建矩阵生成。一次构建多个平台同一个源码树可以同时进行多个原生/交叉构建用于验证对某一平台的改动不会破坏其他平台。Meson 天然支持这一点所有构建产物都被限制在各自的构建目录内只需让多个构建目录共享同一份源码即可。具体做法告诉 Nixpkgs 构建基础设施 Meson 构建目录的位置mesonBuildDirbuild-my-variant-name正常配置configurePhase正常构建buildPhase系统类型System typeNix 使用如下格式的字符串标识它运行的系统类型或平台cpu-os[-abi]该字符串在 Nix 针对特定系统编译时确定其来源是 Meson 的host_machine信息host_machine.cpu_family()、host_machine.endian()、host_machine.cpu()等。出于历史原因和向后兼容性部分 CPU 与 OS 标识符会被翻译映射对应关系如下host_machine.cpu_family()host_machine.endian()Nix 系统类型x86i686armhost_machine.cpu()ppclittlepowerpcleppc64littlepowerpc64leppcbigpowerpcppc64bigpowerpc64mipslittlemipselmips64littlemips64elmipsbigmipsmips64bigmips64使用 Meson 交叉编译的注意点当用 Meson 为本地开发做交叉编译时需要通过--cross-file指定一个 cross-file它定义目标架构与工具链而当“用 Nix 交叉编译 Nix”时Nixpkgs 会自动处理这一切无需手动提供 cross-file。编译环境Compilation environmentsNix 可以用多种编译环境构建这些环境由 flake.nix 中的stdenvs列表定义stdenv默认环境gccStdenv强制使用 gcc 编译器clangStdenv强制使用 clang 编译器ccacheStdenv启用 ccache 编译器缓存以加速编译flake 中还存在libcxxStdenv即使用 libc 标准库的变体使用 flake 构建$ nix build .#nix-cli-ccacheStdenv使用经典 Nix 构建$ nix-build --attr nix-cli-ccacheStdenvnix-cli-ccacheStdenv中的nix-cli是 CLI 组件的名称换成任意其他受支持的环境例如nix-cli-clangStdenv同样有效。从 flake.nix 的构建矩阵可以看到每个组件都会生成组件-stdenv名形式的属性同时还有-static静态链接、-llvmLLVM 工具链等变体。编辑器集成Editor integration基于 clang 的 devShell如.#native-clangStdenv默认安装了clangdLSP 服务器。要让编辑器获得完整的补全、跳转与诊断能力需要一份compile_commands.json告诉clangd每个文件是如何编译的——Meson 的 configure 步骤总会把它生成在构建目录内。配置要点让编辑器使用.#native-clangStdenvshell 中的clangd可以在 devShell 内启动编辑器也可以借助 nix-direnv 与对应的编辑器插件自动加载环境部分编辑器需要额外插件Visual Studio Code 需要安装 clangd 扩展Emacs如 lsp-mode、Vim如 vim-lsp等则需要通用的 LSP 支持插件编辑器相关的具体配置因人而异这里不再展开。格式化与 pre-commit 钩子一次性格式化可以随时运行仓库自带的格式化脚本对整个代码库执行格式化./maintainers/format.sh该脚本maintainers/format.sh依赖pre-commit与_NIX_PRE_COMMIT_HOOKS_CONFIG环境变量因此推荐通过nix develop -c ./maintainers/format.sh运行脚本内部会循环执行pre-commit run --config $_NIX_PRE_COMMIT_HOOKS_CONFIG --all-files还支持--until-stable参数反复运行直到所有检查通过为止。安装 pre-commit 钩子如果希望每次提交前自动运行格式化检查可在 devShell 内安装钩子pre-commit-hooks-install该命令基于 cachix/git-hooks.nix中启用的检查包括check-merge-conflicts及针对 mergify backport 的自定义冲突检测meson-format使用meson format格式化meson.build/meson.options文件nixfmt格式化 Nix 表达式对tests/functional/lang中格式敏感的测试用例做了排除clang-format格式化 C/C 代码排除测试数据与 vendored 代码shellcheck检查 shell 脚本zizmor静态分析 GitHub Actions 工作流。提交时请留意控制台输出。若钩子失败先运行git add --patch采纳其建议的修改然后再次提交。刷新钩子配置当 pre-commit 配置即 flake 中的pre-commit.settings发生变化时需要刷新本地钩子退出开发 shell重新执行nix develop如果仍在使用 pre-commit 钩子再执行一次pre-commit-hooks-install。VSCode 中的 nixfmt 配置将以下 JSON 写入.vscode/settings.json即可让 VSCode 使用nixfmt格式化 Nix 文件Format Document命令、editor.formatOnSave等都会生效{ nix.formatterPath: nixfmt, nix.serverSettings: { nixd: { formatting: { command: [ nixfmt ], }, }, nil: { formatting: { command: [ nixfmt ], }, }, }, }常见问题与要点回顾Windows 检出失败克隆前务必开启core.symlinks。checkPhase跑什么它执行 Meson 的测试阶段覆盖单元测试unit-tests、功能测试functional-tests见 tests/functional、JSON schema 校验json-schema-checks见 src/json-schema-checks等各开关的默认值可在根目录 meson.options 中查到。改代码后快速重编优先使用ccacheStdenv变体并在 Linux 上享受默认的 mold 链接器加速。构建产物位置Meson 将产物限制在构建目录build或mesonBuildDir指定的目录内源码树保持干净这也是支持多平台并行构建的基础。想深入测试体系参见doc/manual/source/development/testing.md并阅读 ci/gha/tests 了解 CI 中的测试编排方式。通过本文的指引你应该已经能够用经典或 flake 方式进入 Nix 的开发环境、完成构建/测试/安装、为其他平台交叉编译、切换不同的编译器环境并配置好编辑器与提交前的格式化检查从而顺畅地参与到 Nix 自身的开发中。【免费下载链接】nixNix, the purely functional package manager项目地址: https://gitcode.com/gh_mirrors/ni/nix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表