Nix Flakes 及其在 Guix 中的对应物

Lobsters Hottest 工具

摘要

详细比较 Nix Flakes 与 Guix 包管理系统中的对应物,涵盖依赖声明、锁定、纯净性、输出、开发环境和系统配置。

<p><a href="https://lobste.rs/s/fybei2/nix_flakes_their_guix_equivalents">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/12 14:54

# Nix Flakes 及其 Guix 对应功能 来源:https://coopi.neocities.org/posts/nix-flakes-vs-guix ## 目录 - 1. 本文面向谁? - 2. 到底什么是 flake? - 3. 声明依赖:inputs 对比 channels - 3.1. Flakes:`inputs` - 3.2. Guix:channels - 3.3. 对比 - 4. 锁定依赖:flake.lock 对比 guix describe - 4.1. Flakes:`flake.lock` - 4.2. Guix:`guix describe` 和 `guix time-machine` - 4.3. 对比 - 5. 纯净性:强制隔离 - 5.1. Flakes:纯求值模式 - 5.2. Guix:设计上的纯净性 - 5.3. 对比 - 6. 输出模式:你的项目产出什么 - 6.1. Flakes:结构化输出 - 6.2. Guix:一等公民的记录和模块 - 6.3. 对比 - 7. 开发环境:devShells 对比 manifests - 7.1. Flakes:`devShells` - 7.2. Guix:`guix shell` 和 manifests - 7.3. 对比 - 8. 系统配置:`nixosConfigurations` 对比 `operating-system` - 8.1. Flakes:`nixosConfigurations` - 8.2. Guix:`operating-system` - 8.3. 对比 - 9. 那么 Guix 没有哪些功能? - 9.1. 标准的项目入口点 - 9.2. 注册表和快速安装语法 - 9.3. `nix flake show` - 10. Guix 有哪些 flakes 没有的功能? - 10.1. `guix time-machine` - 10.2. Grafting(嫁接) - 10.3. 一等公民的包记录 - 10.4. 全源码引导 - 10.5. 频道认证 - 11. 总结表格 - 12. 那么……谁赢了? - 13. 没错,我真的引用了来源 :3 我想告诉你一个我花了很长时间才真正理解的秘密:**根本不存在所谓“flake 的 Guix 对应物”**。我知道!听起来像在敷衍!但请听我说完——这实际上是整个对比中最有趣的部分。Flake 是一个大功能,一次性解决了很多问题(Dolstra, 2020)。Guix 也解决了同样的问题,但它用的是多个更小、更正交的工具,而且这些工具在 flakes 出现之前就已经存在了(Contributors, 2025a)。所以映射关系并不是 flake → ??? —— 而是 flake → channels,**以及** manifests,**以及** `guix describe`,**以及** `guix shell`,**以及** `operating-system` 声明,**以及**……好吧,我有点超前了。让我先铺垫一下背景。 ## 1. 本文面向谁? 如果你是 Nix 用户,你可能知道 Guix 是“另一个函数式包管理器,那个用 Scheme 的”。你或许听说过它被描述为 Nix 的一个分支——它们确实有共同的血统!Guix 重用了 Nix 守护进程(处理构建隔离和存储管理的 C++ 组件),其余部分则用 Guile Scheme 从头构建((rekado), 2019)。派生格式(ATerm)是共享的(Khana, 2026),守护进程的谱系是共享的,但语言、包定义、服务系统,以及守护进程之上的整个世界——这些都是 Guix 自己的东西((rekado), 2020)。 如果你是 Guix 用户,你可能看到 Nix 用户谈论“flakes”,并好奇这有什么大不了的。或许你曾闪过一丝疑虑——等等,我们没有那个功能吗?我们落后了吗?这两个问题的答案都是:放松点!Guix 拥有 flakes 提供的**所有能力**,只是以不同的形式呈现。理解这种形式正是本文的目的。 ## 2. 到底什么是 flake? 给在场的 Guix 朋友们一个简短的入门介绍!(Nix 用户可以跳过这部分——或者也读一下,有时复习一下也不错 :3) Nix flake 核心上就是一个源树——通常是一个 Git 仓库——在其根目录下包含一个名为 `flake.nix` 的文件。仅此而已。`flake.nix` 的存在就使其成为 flake(Project, 2024)。该文件具有特定的结构: ```nix { # 这个 flake 提供了什么的人类可读描述。 description = "一个简单的 Go Web 服务器"; # 依赖——其他 flake、Git 仓库、压缩包。Nix 会获取它们, # 对它们求值,并将它们传递给 outputs。 inputs = { # "github:NixOS/nixpkgs/nixos-unstable" 表示:从 github.com/NixOS/nixpkgs 获取 nixos-unstable 分支 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; # outputs 是一个函数。它的参数是解析后的 inputs(加上特殊的 "self" input,即这个 flake 本身)。 # 它返回一个属性集,包含这个 flake 提供的所有东西。 outputs = { self, nixpkgs }: let # 辅助函数,用于为多个 CPU 架构生成输出,无需重复编写。 # 没有这个的话,你需要为每个平台分别写 packages.x86_64-linux、packages.aarch64-linux 等。 supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; in { # 这个 flake 可以构建的包。nix build .#myapp 会在这里查找。 # system 层级是必须的——flake 要求明确指定平台。 packages = forAllSystems (system: let # 为当前系统导入 nixpkgs。 # 这让我们可以访问完整的 nixpkgs 包集合——stdenv、buildGoModule,以及大约 12 万个其他包 [cite:@repology]。 pkgs = nixpkgs.legacyPackages.${system}; in { # "default" 意味着 nix build .(不指定名称)会构建这个包。 default = pkgs.buildGoModule { pname = "myapp"; version = "0.1.0"; src = ./.; # 整个 Git 仓库作为源 }; }); # 开发 shell。nix develop 会在这里查找。可以将其理解为:一个包含所有构建工具的可复现 shell。 devShells = forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; in { default = pkgs.mkShell { # 这些包在 shell 内将可通过 PATH 获取。 buildInputs = with pkgs; [ go gopls gotools ]; }; }); }; } ``` 三个主要部分: - `description`:一个人可读的字符串,不言自明。 - `inputs`:你的依赖——其他 flake、Git 仓库、压缩包。Nix 会获取它们,锁定它们,并将它们传递给 outputs 函数。 - `outputs`:一个函数,接收所有已解析的 inputs,并产生一个结构化的属性集,包含各种**东西**——包、开发 shell、NixOS 配置、overlay 等(Project, 2024)。 当你对某个 flake 运行任何 `nix` 命令时,Nix 还会生成一个 `flake.lock`——一个 JSON 文件,它会锁定每个 input 及其 transitive inputs 到精确的修订版本。这个锁文件使得构建可以在不同机器和不同时间点上复现。 Flake 还强制**纯净求值**:没有 `$NIX_PATH`,没有 `builtins.currentSystem`,没有环境变量泄露进来。一切都必须是显式的(Contributors, 2026)。 所以,总结一下,flakes 大致做了六件事: 1. **声明依赖**(`inputs`) 2. **锁定依赖**(`flake.lock`) 3. **强制纯净性**(没有隐式状态) 4. **提供标准的输出模式**(`packages`、`devShells`、`apps`、`nixosConfigurations` 等) 5. **支持可复现的分享**(任何人都可以 `nix build github:you/your-repo`) 6. **定义开发环境**(`devShells`) 现在关键来了:Guix 在 flakes 于 2021 年 11 月 1 日作为 Nix 2.4 的一部分引入之前,就已经有了针对其中大多数问题的解决方案(Project, 2021)。通道机制大约在 2018–2019 年出现在 Guix 中(Contributors, 2025a)。而且这些解决方案是**正交的**——你可以独立使用每一个,无需绑定一个单一的、庞大的抽象。 让我们逐一过一遍!这是有趣的部分! ## 3. 声明依赖:inputs 对比 channels ### 3.1. Flakes:`inputs` 在 flake 中,你直接在 `flake.nix` 中声明你的依赖: ```nix inputs = { # 从 GitHub 的 25.11 发布分支拉取 nixpkgs nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; home-manager = { url = "github:nix-community/home-manager"; # "follows" 表示:不要获取 home-manager 自己的 nixpkgs input——而是使用**我们的** nixpkgs。 # 这可以避免同时存在两个不同版本的 nixpkgs,这是导致奇怪冲突的常见原因。 inputs.nixpkgs.follows = "nixpkgs"; }; }; ``` 每个 input 可以是另一个 flake 或非 flake 源。Nix 会获取它们全部,进行求值,并将结果传递给 `outputs` 函数(Project, 2024)。 ### 3.2. Guix:channels Guix 自 2018–2019 年左右就有了[通道](https://guix.gnu.org/manual/en/html_node/Channels.html)(Contributors, 2025a)。一个通道就是一个包含 Guile 模块的 Git 仓库——通常是包定义,也可以是服务、系统配置或任何你想用的 Scheme 代码。你在 `~/.config/guix/channels.scm` 中声明你的通道: ```scheme ;; channels.scm 是一个 Scheme 文件,返回一个通道记录列表。 ;; 每个通道是一个 Git 仓库,Guix 会获取并编译它。 (list (channel (name 'guix) ; 官方 Guix 通道 (url "https://git.guix.gnu.org/guix.git") (branch "master")) (channel (name 'my-packages) ; 你的个人通道 (url "https://example.com/me/my-guix-packages.git") (branch "main"))) ``` 运行 `guix pull` 会获取所有通道,编译它们,并使它们的模块可用于每个 `guix` 命令。这就是你的依赖解析步骤。 通道可以使用仓库根目录下的 `.guix-channel` 文件来声明对其他通道的依赖(Contributors, 2025b): ```scheme ;; .guix-channel — 位于通道仓库根目录。 ;; 这告诉 Guix,这个通道依赖于另一个名为 'nonguix' 的通道, ;; 因此 guix pull 会同时获取两者。 (channel (version 0) (dependencies (channel (name 'nonguix) (url "https://gitlab.com/nonguix/nonguix")))) ``` 这大致类似于 flake 中的 `inputs`——一个通道可以拉入另一个通道。当你运行 `guix pull` 时,所有传递的通道依赖都会一起被获取。 ### 3.3. 对比 两个系统都允许你声明外部依赖并自动拉取它们。主要区别有: - Flakes 是**每个项目**一个——每个仓库有自己的 `flake.nix` 和自己的 inputs。通道是**系统范围**或每个用户的——你的 `channels.scm` 适用于所有 `guix` 调用。这意味着 flakes 天然支持不同项目使用不同的依赖集合;而在 Guix 中,你通常需要使用 `guix time-machine` 或单独的配置文件来实现相同的效果。 - Flakes 使用**类似 URL 的语法**来引用(`github:NixOS/nixpkgs`、`git+https://...`),而通道使用**纯 Git URL**。Flake 语法在快速引用时更简洁,但通道更简单、更显式。 - Flakes 支持**非 flake 输入**(`flake = false;`)用于不包含 `flake.nix` 的仓库。在 Guix 中,一个通道**就是**一个包含 Scheme 文件的 Git 仓库——不需要特殊的选择加入。任何包含 Guile 模块的仓库都可以成为一个通道。 ## 4. 锁定依赖:flake.lock 对比 guix describe ### 4.1. Flakes:`flake.lock` 锁文件是一个 JSON 图。每个 input 都被锁定到精确的提交哈希,并且 Nix 会验证 `narHash`(完整源树的哈希)与它获取的内容是否一致(Project, 2024)。锁文件被提交到仓库中,因此任何克隆仓库的人都会得到完全相同的依赖版本。 ```json { "nodes": { "nixpkgs": { "locked": { "lastModified": 1712981134, "narHash": "sha256-...", "rev": "0d70582...", "type": "github" }, "original": { "id": "nixpkgs", "type": "indirect" } } }, "version": 7 } ``` `original` 是你要求的内容。`locked` 是你**实际得到**的内容。这个两层系统意味着你可以有选择地更新——`nix flake lock --update-input nixpkgs` 只更新那一个输入,同时保持其他输入不变。 (继续翻译剩余部分,保持格式一致)

相似文章

GuixPkgs:每个 Guix 包,作为 Nix flake

Lobsters Hottest

GuixPkgs 是一个项目,它将每个 GNU Guix 包以 Nix flake 的形式提供,使用户能够在单个 flake 中混合使用 Guix 和 Nixpkgs 包。它使用 guix-transfer 将 Guix 派生转换为 Nix 派生,并提供二进制缓存以避免完全重建 Guix 引导过程。

删除我的 Nix Flakes 与 Guix 对比帖子

Lobsters Hottest

作者删除了他们关于比较 Nix Flakes 和 Guix 等效物的人气博文,此前 Andrew Tropin 指责其使用 LLM 撰写该文。作者表示十分沮丧,并决定移除该帖子。

Nixmac

Product Hunt

Nixmac 是一个为 Nix-darwin 提供简洁英文界面的工具,简化了 macOS 上的 Nix 配置。

Nix Evaluation Is a Scheduling Problem

Lobsters Hottest

The article argues that Nix evaluation is fundamentally a scheduling problem and introduces Evix, a library-first async Nix evaluation engine designed for persistent, structured evaluation results.