Nix 源码构建开发指南:从 devShell 到交叉编译的完整实践
2026/9/21 2:21:46 网站建设 项目流程

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-shell

Nix 仓库在根目录提供了 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]$ dontAddPrefix=1 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 已经启用了flakesnix-command两个实验特性。这两个特性分别对应仓库文档中定义的实验特性开关(xp-feature-flakesxp-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/prefix,devShell 的shellHook已经接管了这些变量(见 packaging/dev-shell.nix 中的shellHook定义)。

构建发布版:

$ nix build

关于如何运行与过滤各类测试(单元测试、功能测试、模糊测试等),可参考仓库中的测试运行文档doc/manual/source/development/testing.md,测试基础设施位于 tests/functional 与各*-tests组件目录。

devShell 的底层实现

理解nix develop背后发生了什么,有助于排查环境问题。从 flake.nix 可以看到:

  • devShellsmakeShell(定义于 packaging/dev-shell.nix)针对每个系统与每种 stdenv 生成,并统一以native-<stdenv名>前缀命名,默认的native实际指向native-stdenv
  • devShell 本质是一个stdenv.mkDerivationpname = "shell-for-nix"),其shellHook会依次设置PATH、清空PYTHONPATH、导出MANPATH,并重定义configurePhase/buildPhase/checkPhase/installPhase,使其默认进入build目录后调用 Meson/Ninja 对应的阶段函数;
  • 所有内部组件(nix-utilnix-storenix-expr等,见 meson.build 中的 subproject 列表)的buildInputs会通过buildInputsClosureCond做闭包去重后注入 shell,避免重复引入内部依赖;
  • 在 Linux 上,CC_LD/CXX_LD被设为mold,即默认使用 mold 链接器加速链接;此外 shell 中还预装了pre-commitnixfmtshellcheckinclude-what-you-usegdb(Unix)、clang-tools(clang shell)等开发工具。

支持的平台

Nix 可构建的官方平台在 flake.nix 的systems列表与文档中均有列出:

  • x86_64-linux
  • i686-linux
  • aarch64-linux
  • aarch64-darwin
  • armv6l-linux
  • armv7l-linux
  • powerpc64-linux(ELFv1 ABI)
  • powerpc64le-linux
  • riscv64-linux

注意:当前 flake.nix 中systems实际只包含i686-linuxx86_64-linuxaarch64-linuxaarch64-darwin,其余平台(如armv6l-linuxpowerpc64-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-linux
  • armv7l-linux
  • powerpc64-linux(ELFv1 ABI)
  • powerpc64le-linux
  • riscv64-linux
  • x86_64-freebsd
  • x86_64-w64-mingw32(Windows 目标,flake 中为其配置了 wine 模拟器)

如果想要在尚未支持的平台上引导(bootstrap)Nix,可以向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 天然支持这一点:所有构建产物都被限制在各自的构建目录内,只需让多个构建目录共享同一份源码即可。

具体做法:

  1. 告诉 Nixpkgs 构建基础设施 Meson 构建目录的位置:

    mesonBuildDir=build-my-variant-name
  2. 正常配置:

    configurePhase
  3. 正常构建:

    buildPhase

系统类型(System type)

Nix 使用如下格式的字符串标识它运行的系统类型(或平台):

<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 系统类型
x86i686
armhost_machine.cpu()
ppclittlepowerpcle
ppc64littlepowerpc64le
ppcbigpowerpc
ppc64bigpowerpc64
mipslittlemipsel
mips64littlemips64el
mipsbigmips
mips64bigmips64

使用 Meson 交叉编译的注意点

当用 Meson 为本地开发做交叉编译时,需要通过--cross-file指定一个 cross-file(它定义目标架构与工具链);而当“用 Nix 交叉编译 Nix”时,Nixpkgs 会自动处理这一切,无需手动提供 cross-file。

编译环境(Compilation environments)

Nix 可以用多种编译环境构建,这些环境由 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-ccacheStdenv

nix-cli-ccacheStdenv中的nix-cli是 CLI 组件的名称;换成任意其他受支持的环境(例如nix-cli-clangStdenv)同样有效。从 flake.nix 的构建矩阵可以看到,每个组件都会生成<组件>-<stdenv名>形式的属性,同时还有-static(静态链接)、-llvm(LLVM 工具链)等变体。

编辑器集成(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)发生变化时,需要刷新本地钩子:

  1. 退出开发 shell,重新执行nix develop
  2. 如果仍在使用 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 将产物限制在构建目录(buildmesonBuildDir指定的目录)内,源码树保持干净,这也是支持多平台并行构建的基础。
  • 想深入测试体系:参见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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询