Nix Flakes 及其在 Guix 中的对应物
摘要
详细比较 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
GuixPkgs 是一个项目,它将每个 GNU Guix 包以 Nix flake 的形式提供,使用户能够在单个 flake 中混合使用 Guix 和 Nixpkgs 包。它使用 guix-transfer 将 Guix 派生转换为 Nix 派生,并提供二进制缓存以避免完全重建 Guix 引导过程。
Guix Nix 的怪异融合:在 Nix 中利用 Guix 派生
一项技术探索,展示了 Nix 如何构建 Guix 派生项,强调了共享底层“输入输出机”架构以及跨生态系统互操作的可能性。
删除我的 Nix Flakes 与 Guix 对比帖子
作者删除了他们关于比较 Nix Flakes 和 Guix 等效物的人气博文,此前 Andrew Tropin 指责其使用 LLM 撰写该文。作者表示十分沮丧,并决定移除该帖子。
Nixmac
Nixmac 是一个为 Nix-darwin 提供简洁英文界面的工具,简化了 macOS 上的 Nix 配置。
Nix Evaluation Is a Scheduling Problem
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.