文章介绍 Nix 包管理工具的实验性功能 Nix flakes
来源标题:Understanding Nix Flakes
Tyler Langlois 撰文介绍了 Nix 包管理工具的 flakes 功能,该功能旨在以更自包含、可复现的方式构建项目。尽管仍处于实验阶段,已有越来越多开源项目发布 flake.nix 文件来提供预定义开发环境和构建自动化。文章指出 flakes 历史复杂、稳定化路径尚不明确,但对采用它的项目仍有实用价值。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
本文由 Tyler Langlois 投稿
Nix flakes 是 Nix 包管理工具的一项实验性功能,旨在以更加自包含、可复现的方式构建项目。尽管该功能仍处于实验阶段,但越来越多的开源项目开始发布 flake.nix 文件,借此提供预定义的开发环境和构建自动化,即便这些仓库可能并未为 NixOS Linux 发行版提供上游软件包或模块。虽然 Nix flakes 的历史颇为曲折,稳定化之路也尚不明朗,但对于选择引入 flakes 的项目而言,它确实能提供有用的能力。
flakes 的承诺在于为获取和构建软件提供可靠且可复现的方法。假设某个开源项目想为 Linux 用户提供一种简便的方式来搭建其软件的开发环境。各发行版往往使用不同版本的编程语言、库等,这意味着 Debian 13 上的用户与 Fedora 45 上的用户所拥有的软件版本会略有差异。有些依赖甚至根本没有被打包进发行版。这对所有相关方来说都是件头疼的事。
项目可以通过发布一个 Nix flake 来避免这一烦恼:该 flake 固定其依赖的版本,让每位用户无论运行哪个发行版都能安装相同的软件。最终用户通常只需一个本地(且配置正确)的 Nix 安装以及极少(甚至没有)其他工具链要求,就能可靠地安装依赖、构建项目并运行测试。
flakes 用 Nix 语言编写,主要由 Nix 包管理器使用。虽然 NixOS 可以通过 nixos-rebuild 等工具使用 flakes,但 flakes 独立于 NixOS 运作。flake.nix 文件是 flakes 工作机制的核心:这些文件包含以类 URL 远程资源(通常是软件仓库)形式定义的 inputs,以及 flake 使用者可以使用的 outputs,例如软件包或针对这些包的测试。
一个简单的 flake 可能如下面的 Nix 代码片段所示:
inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; }; outputs = { nixpkgs, ... }: { packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; };
legacyPackages 这个名字有点误导性,因为其中的 Nix 包并非字面意义上的遗留包:这个名字是一个兼容性垫片,用于避免 nix flake show 之类的命令在终端中列出超过 100,000 个包的情况。当使用 legacyPackages 而非 packages 时,nix 命令在打印其值时会避免列出所有包,具体如上游文档所述。
在向 flake.nix 文件添加名为 nixpkgs 的 input 后,nix flake lock 会创建一个 flake.lock 文件,将 nixpkgs input 固定到 nixpkgs-unstable 分支所引用的提交对应的 NixOS/nixpkgs 仓库上。就像 Rust 项目中的 Cargo.lock 文件或 Ruby 项目中的 Gemfile.lock 文件一样,flake.lock 这个 JSON 文件将每个 input 锚定到一个具体的上游修订版本。这样做使得 flake 能够一致地引用其依赖,并以一组已知兼容的 inputs 可靠地构建其 outputs。
将 flake 的 inputs 锁定到分支的最新提交是表达上游引用的一种方式,但 flake 引用格式还允许其他几种策略,包括指定特定标签或绝对路径等其他形式。锁定的修订版本会成为仓库的一部分,之后可以用 nix flake update 命令更新(前提是该引用指向的是分支名之类的非具体目标,而非 Git 哈希)。
flake 的 outputs 属性写成一个 Nix 函数。当 Nix 处理 flake.nix 文件时,会先获取 inputs,然后将其传递给 outputs 函数,在那里它才能真正使用来自 nixpkgs 等 inputs 的具体值。在这个例子中,该 flake 只是提供从 nixpkgs-unstable 分支取来的 hello 包。
默认情况下,Nix 假定 inputs 下的每个引用自身都包含自己的 flake.nix,因此也拥有自己的一组 outputs。当在 outputs 函数中引用各个 input(如示例代码中的 nixpkgs.legacyPackages)时,使用的正是这些值。
这个例子演示了如何将软件包打包进 Nix flake,但 flakes 的 outputs 中还有更多顶层属性在出现时可提供其他用途。例如,devShells 提供预置了库和环境变量的开发环境,而 nixosModules 可以导入到 NixOS 配置中使用。nix 命令行工具能识别许多属性作为 flake schema 的一部分,不过在这些约定名称之外,flake 的 outputs 结构是任意的。
锁定的 inputs 是 flakes 的一个重要特性,但另一个可以说同样重要的行为是:flakes 在纯求值模式下进行求值,这意味着默认禁止那些可能改变 Nix 计算值的外部因素。例如,不得读取环境变量,网络访问则通过校验和检查下载的产物来加以限制。虽然可以通过 --impure 标志放宽这一行为,但强制执行它提升了 flake 的可复现性。
需要注意的是,Nix flakes 的某些功能也可以由其他库或工具实现。例如,锁定依赖(类似于 Nix flake 的 input)可以在不使用 flakes 的情况下通过 niv 实现,该项目提供了类似 flake.lock 文件的能力。开发 shell 可由现有的非实验性命令配合 shell.nix 文件来支持。对许多用户而言,Nix flakes 更像是为 inputs、锁文件和 Nix 命令等实用概念提供的一个更通用、更统一的伞状框架。
在源代码仓库中遇到 flake.nix 文件时,该如何使用它?首先,必须安装 nix 工具,并且 Nix flakes
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。