Flakes
Nix flakes 是 Nix 2.4 版本中首次引入的一项实验性功能[1][2],旨在解决 Nix 生态系统许多领域的改进问题:它们为 Nix 项目提供了一个统一结构、允许固定每个依赖项的特定版本并通过锁文件共享这些依赖项,同时总体上使编写可复现的 Nix 表达式变得更加方便。
Flake 是一个直接包含 flake.nix
文件的目录,该文件内容遵循一种特定结构。Flakes 引入了一种类似 URL 的语法[3] 来指定远程资源。为了简化这种 URL 语法,Flakes 使用符号标识符注册表[4],这允许通过类似 github:NixOS/nixpkgs
的语法直接指定资源。
Flakes 还允许锁定引用和版本,然后通过 inputs [5][6] 以可编程方式进行查询和更新。此外,一个实验性的 CLI 实用程序接受 flake 引用作为参数,该引用指向用于构建、运行和部署软件包的表达式。[7]
Flake 文件结构
一个最小化的 flake 文件包含该 flake 的描述(description),一组输入依赖项(inputs)和一个输出(outputs)。您可以随时使用 nix flake init
命令来生成一个非常基础的 flake 文件。这将在当前目录下创建一个名为 flake.nix
的文件,其内容类似于:
{
description = "一个非常基础的 flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }: {
packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
};
}
在上述示例中,您可以看到对该 flake 的描述、指定为某 Github 仓库特定分支的输入(此为 nixos/nixpkgs
仓库的 nixos-unstable
分支)以及一个使用该输入的输出。该输出简单地指定了该 flake 包含一个用于 x86_64 架构名为 hello
的包。即使您的 flake 输出不使用其输入(尽管这在实践中极不可能),其输出仍需要是一个 Nix 函数。
Nix 配置
为了推导 flake,您可以覆盖 nix.conf
文件中设置的全局 Nix 配置。例如,这可用于设置特定项目的二进制缓存源,同时保持全局配置不变。Flake 文件中可包含一个 nixConfig 属性,并在其中设置相关配置。例如,启用 nix-community 二进制缓存可以通过以下方式实现:
{
...
nixConfig = {
extra-substituters = [
"https://nix-community.cachix.org"
];
extra-trusted-public-keys = [
"nix-community.cachix.org-1:...="
];
}
}
nix.settings
下,而不是 nix
下。例如,您无法在 nix.optimization.enable
下指定自动存储优化。
设置
临时启用 Flakes
当使用任意 nix
命令时,添加如下命令行参数:
--experimental-features 'nix-command flakes'
永久启用 Flakes
NixOS
添加如下内容至 NixOS 配置:
nix.settings.experimental-features = [ "nix-command" "flakes" ];
Home Manager
添加如下内容至您的 home manager 配置:
nix.settings.experimental-features = [ "nix-command" "flakes" ];
Nix 独立程序
添加如下内容至 ~/.config/nix/nix.conf
或 /etc/nix/nix.conf
:
experimental-features = nix-command flakes
用法
git
管理您的 flake,请确保在首次创建之后使用 git add
添加所有项目文件。Nix Flakes 命令
- Main article: Nix (command)
nix flake
的子命令在
Nix 手册命令参考页面 中被描述。
此 flake 生成一个单 Flake 输出 packages
。其中,x86_64-linux
是系统特定的属性集。其中包含两个软件包的 Derivations(派生/定义):default
和 hello
。您可以使用
show 命令 给出某 flake 的输出,如下所示:
$ nix flake show
└───packages
└───x86_64-linux
├───default: package 'hello-2.12.2'
└───hello: package 'hello-2.12.2'
开发环境 Shell
devShell
是定义在 flake 中由 Nix 提供的开发环境。它允许您声明一个可复用的 Shell 环境,其中将包含开发特定项目所需的工具、库和环境变量。这相当于在 flake 中定义一个 nix-shell
。
{
description = "带有 devShell 的示例 flake";
inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
outputs = { self, nixpkgs}:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
devShells.x86_64-linux.default = pkgs.mkShell {
buildInputs = with pkgs; [
hello
];
shellHook = ''
echo "欢迎进入 devShell!"
'';
};
};
}
进入开发环境 Shell:
$ nix develop
在 flake 仓库中构建特定属性
运行 nix build
将在 legacyPackages
和 packages
输出属性中查找相应的 derivation,然后基于您的系统架构构建默认输出项。如果您想在 flake 仓库中指定构建属性,可以运行 nix build .#<attr>
。在上面的示例中,如果您想构建 packages.x86_64-linux.hello
属性,请运行:
$ nix build .#hello
同样,您可以给 run
命令:nix run .#hello
和 develop
命令:nix develop .#hello
指定属性。
Flake 规范
flake.nix
文件是一个具有特殊限制的 Nix 文件(稍后会详细介绍)。
它有 4 个顶级属性:
description
:描述此 flake 的字符串。
inputs
:一个包含此 flake 所有依赖项的属性集。相关规范见下述内容。
outputs
: 一个接收参数的函数,其参数为所有所需输入的属性集,并输出另一个属性集,其规范如下所述。
nixConfig
:一个属性集,包含了 赋予 nix.conf 的值。这可以通过添加特定于 flake 的配置(例如 二进制缓存源)来扩展用户 nix 操作的正常行为。
输入规范
inputs
属性定义了 flake 的依赖项。例如,为了让系统能够正确构建,nixpkgs 必须被定义为系统 flake 的依赖项。
Nixpkgs 可使用以下代码进行定义:
inputs.nixpkgs.url = "github:NixOS/nixpkgs/<branch name>";
Nixpkgs can alternatively also point to an url cached by the NixOS organization:
inputs.nixpkgs.url = "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz";
In this example the input would point to the `nixpkgs-unstable` channel.
对于任何包含 flake.nix 文件的仓库,其所属网站也必须被定义。Nix 知道 nixpkgs 仓库的位置,因此没有必要声明它在 GitHub 上。
例如,将 Hyprland 添加为输入看起来像这样:
inputs.hyprland.url = "github:hyprwm/Hyprland";
如果您想让 Hyprland 的 nixpkgs 依赖跟随 nixpkgs 输入以避免出现多个版本的 nixpkgs,可以使用以下代码来完成:
inputs.hyprland.inputs.nixpkgs.follows = "nixpkgs";
使用大括号 ({}),我们可以缩短这些内容并将其放在一个表中。代码如下所示:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/<branch name>";
hyprland = {
url = "github:hyprwm/Hyprland";
inputs.nixpkgs.follows = "nixpkgs";
};
};
默认情况下,包 src
中的 Git 子模块不会被复制到 Nix Store,这可能会导致构建失败。Git 仓库中的 Flakes 可以声明它们需要启用 Git 子模块。从 Nix 版本 2.27 开始,您可以通过以下方式启用子模块:
inputs.self.submodules = true;
输出规范
Nix 包管理器仓库的 src/nix/flake-check.md 中对此进行了描述。
一旦 Inputs 被解析,它们就会与 self
一起传递给函数 outputs
,self
是此 flake 在 Store 中的目录。outputs
根据以下规范返回 flake 的输出。
其中:
<system>
为类似“x86_64-linux”、“aarch64-linux”、“i686-linux”、“x86_64-darwin”的值
<name>
是一个属性名称,如“hello”。
<flake>
是一个 flake 名称, 如“nixpkgs”。
<store-path>
是/nix/store..
的路径。
{ self, ... }@inputs:
{
# Executed by `nix flake check`
checks."<system>"."<name>" = derivation;
# Executed by `nix build .#<name>`
packages."<system>"."<name>" = derivation;
# Executed by `nix build .`
packages."<system>".default = derivation;
# Executed by `nix run .#<name>`
apps."<system>"."<name>" = {
type = "app";
program = "<store-path>";
};
# Executed by `nix run . -- <args?>`
apps."<system>".default = { type = "app"; program = "..."; };
# Formatter (alejandra, nixfmt or nixpkgs-fmt)
formatter."<system>" = derivation;
# Used for nixpkgs packages, also accessible via `nix build .#<name>`
legacyPackages."<system>"."<name>" = derivation;
# Overlay, consumed by other flakes
overlays."<name>" = final: prev: { };
# Default overlay
overlays.default = final: prev: { };
# Nixos module, consumed by other flakes
nixosModules."<name>" = { config, ... }: { options = {}; config = {}; };
# Default module
nixosModules.default = { config, ... }: { options = {}; config = {}; };
# Used with `nixos-rebuild switch --flake .#<hostname>`
# nixosConfigurations."<hostname>".config.system.build.toplevel must be a derivation
nixosConfigurations."<hostname>" = {};
# Used by `nix develop .#<name>`
devShells."<system>"."<name>" = derivation;
# Used by `nix develop`
devShells."<system>".default = derivation;
# Hydra build jobs
hydraJobs."<attr>"."<system>" = derivation;
# Used by `nix flake init -t <flake>#<name>`
templates."<name>" = {
path = "<store-path>";
description = "template description goes here?";
};
# Used by `nix flake init -t <flake>`
templates.default = { path = "<store-path>"; description = ""; };
}
您还可以定义其他任意属性,但以上这些是 Nix 已知的输出。
核心使用模式
使您的推导更纯
Nix Flakes 在纯粹推导模式下进行,这意味着对于外部环境的访问被限制以确保可复现性。要保持使用 Flakes 时的纯粹性(Purity),请考虑以下方式:
builtins.currentSystem
函数是非确定且不纯的,因为它反映了执行推导的主机系统。通常可以通过将系统类型(例如 x86_64-linux)显式传递给需要它的 Derivations 来避免这种情况。
builtins.getEnv
函数也是不纯的。请避免从环境变量中读取数据,同样,也不要引用 flake 目录之外的文件。
为多架构定义 Flake
Flakes 强制要求您为每种支持的架构指定一个程序。以下示例展示了如何编写一个针对多种架构的 flake。
{
description = "针对多种架构的 flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }: let
systems = [ "x86_64-linux" "aarch64-linux" ];
forAllSystems = f: builtins.listToAttrs (map (system: {
name = system;
value = f system;
}) systems);
in {
packages = forAllSystems (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
hello = pkgs.hello;
default = pkgs.hello;
});
};
}
您还可以使用如 flake-utils 或 flake-parts 的第三方项目来编写,它们会提供代码来避免此类样板代码。为了避免多次重新定义程序,请参阅 Flake Utils#Defining a flake for multiple architectures
使用 overlays
要将 Overlays 与 Flakes 一起使用,请参阅 Overlays#In a Nix flake 页面。
启用非自由软件
为了在 flake 项目中允许使用 非自由软件,您需要在导入 Nixpkgs 时通过设置 config.allowUnfree = true;
来明确允许它。
{
inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
outputs = { self, nixpkgs, flake-compat }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; config.allowUnfree = true;};
in {
...
};
}
启用 Flake 的 NixOS 配置
可使用 Flakes 管理 NixOS 系统配置,以获得可复现、声明式输入和简化更新的优点。
关于详情与示例,请参阅 NixOS system configuration#Defining NixOS as a flake。
开发技巧
使用 direnv 自动切换 Nix shell
在项目目录之间导航时,使用 Direnv 可以自动激活不同的 Nix shell。Nix 与 Direnv 的额外集成参阅 nix-direnv。
推送 Flakes 至 Cachix
https://docs.cachix.org/pushing#flakes
非 Flake 项目中的 Flake 支持
flake-compat 库提供了一个兼容层,允许使用传统 default.nix
和 shell.nix
文件的项目与 Flakes 兼容。更多详情和使用示例,请参阅 Flake Compat 页面。
另一个允许在非 flake 项目中使用 Flakes 的项目是 flake-inputs。
从 Nix 表达式访问 Flakes
如果您想在启用了 Flakes 功能的系统上从常规 Nix 表达式中访问 flake,可以使用类似 (builtins.getFlake "/path/to/directory").packages.x86_64-linux.default
的代码,其中“directory”是包含 flake.nix
的目录。
高效构建多个 Flake 输出
要自动推送所有 flake 输出,请查看 devour-flake。
构建一个添加至 PR 中的包
nix build github:nixos/nixpkgs?ref=pull/<PR_NUMBER>/head#<PACKAGE>
这允许构建尚未添加到 nixpkgs 的包。
请注意,这将下载 nixpkgs 的完整 tarball 压缩档。如果您已有本地克隆,由于增量压缩机制,使用它可能会更快:
git fetch upstream pull/<PR_NUMBER>/head && git checkout FETCH_HEAD && nix build .#PACKAGE
这允许构建尚未添加到 nixpkgs 的包。
如何在 git 中添加一个本地文件但不将其包含在提交中
当 git 文件夹存在时,flake 将仅复制在 git 中添加的文件,以最大限度地提高可复现性(因此,如果您忘记在代码库中添加本地文件,则在尝试编译时会直接出错)。但是,有时出于开发目的您可能需要创建一个备用的 flake 文件,例如包含您首选编辑器的配置,如此处所述,这种情况下当然无需提交此文件,因为它只包含您自己首选的工具。在上述情况下,您可以执行以下操作(例如,创建了一个名为 extra/flake.nix
的文件):
git add --intent-to-add extra/flake.nix
git update-index --skip-worktree --assume-unchanged extra/flake.nix
直接依赖项的快速迭代
使用 Nix 作为开发环境的一个常见痛点是,每次更新依赖项时都需要完全重构并重新进入开发 shell。nix develop --redirect <flake> <directory>
命令允许您向 shell 提供可变的依赖项,就像它是由 Nix 构建的一样。
考虑这样一个场景:您的可执行程序 consumexe
依赖于一个库 libdep
。你希望同时开发这两个项目,并且对 libdep
的修改能够实时反映到 consumexe
中。这种工作流程可以通过以下方式实现:
cd ~/libdep-src-checkout/
nix develop # Or `nix-shell` if applicable.
export prefix="./install" # configure nix to install it here
buildPhase # build it like nix does
installPhase # install it like nix does
现在您已经构建了依赖项,consumexe
可以将其作为输入。在另一个终端中:
cd ~/consumexe-src-checkout/
nix develop --redirect libdep ~/libdep-src-checkout/install
echo $buildInputs | tr " " "\n" | grep libdep
# Output should show ~/libdep-src-checkout/ so you know it worked
如果 Nix 警告您重定向的 flake 实际上并未用作已推导 flake 的输入,请尝试使用 --inputs-from .
标志。如果一切顺利,您应该能够在依赖项更改时执行 buildPhase && installPhase
操作,并使用新版本依赖重建您的程序,而无需退出开发 shell。
另见
官方来源
- Flakes - nix.dev
- Nix flake 命令参考手册 - 关于 Flakes 及其各部分的更多附加细节。
- RFC 49 (2019) - 原始 Flakes 规范
指南
- Flakes 幻象,亦非洪水猛兽 (Jade Lovelace, 2024)
- NixOS & Flakes Book(Ryan4yin, 2023) - 🛠️ ❤️ 一本非官方的 NixOS & Flakes 新手入门书籍。
- Nix Flakes:一个简要介绍 (Xe Iaso, 2022)
- Practical Nix Flakes (Alexander Bantyev, 2021) - 关于使用 Nix 和 Flakes 的介绍文章。
- Nix Flakes, 第一节:介绍和教程 (Eelco Dolstra, 2020)
- Nix Flakes, 第二节:推导缓存 (Eelco Dolstra, 2020)
- Nix Flakes, 第三节:管理 NixOS 系统 (Eelco Dolstra, 2020)
- Nix flakes 101: Introduction to nix flakes (Jörg Thalheim, 2020) YouTube 视频
Flake 实用模块
- flake-utils:一个用于简化 Flakes 编写、避免样板代码的库
- flake-parts:帮助编写模块化、结构化 Flakes 的库
- flake-compat:Flakes 兼容层
References
- ↑ Nix Reference Manual, §13.8. Experimental Features, 📖︎ flakes subsection
- ↑ Nix Reference Manual, §14.27. 📖︎ Release 2.4 (2021-11-01)
- ↑ Nix Reference Manual, §8.5.17. nix flake, 📖︎ URL-like syntax subsection
- ↑ Nix Reference Manual, §8.5.62. 📖︎ nix registry
- ↑ Nix Reference Manual, §7.5.19. 📖︎ nix flake lock
- ↑ Nix Reference Manual, §7.5.17. 📖︎ nix flake info
- ↑ Nix Reference Manual, §8.5.1. 📖︎ nix