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