name: nix最佳实践 description: 适用于flakes、overlays、非自由软件处理和二进制overlay的Nix模式。在编写flake.nix或shell.nix时使用。
Nix最佳实践
Flake结构
标准flake.nix结构:
{
description = "项目描述";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# 此处添加包
];
};
});
}
Follows模式(避免重复的Nixpkgs)
添加overlay输入时,使用follows共享父级nixpkgs,避免下载多个版本:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# Overlay跟随父级nixpkgs
some-overlay.url = "github:owner/some-overlay";
some-overlay.inputs.nixpkgs.follows = "nixpkgs";
# 通过中间输入链式跟随
another-overlay.url = "github:owner/another-overlay";
another-overlay.inputs.nixpkgs.follows = "some-overlay";
};
所有输入必须在输出函数中列出,即使未直接使用:
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:
应用Overlays
Overlays用于修改或向nixpkgs添加包:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
overlay1.overlays.default
overlay2.overlays.default
# 内联overlay
(final: prev: {
myPackage = prev.myPackage.override { ... };
})
];
};
in
处理非自由软件包
选项1:nixpkgs-unfree(团队推荐)
使用numtide/nixpkgs-unfree处理EULA许可的软件包,无需用户配置:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable";
nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs";
# 非自由overlay跟随nixpkgs-unfree
proprietary-tool.url = "github:owner/proprietary-tool-overlay";
proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree";
};
链式关系:proprietary-tool → nixpkgs-unfree → nixpkgs
选项2:用户配置
用户在~/.config/nixpkgs/config.nix中添加:
{ allowUnfree = true; }
选项3:特定软件包(Flake)
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
"specific-package"
];
};
in
注意:flake.nix中的config.allowUnfree不适用于nix develop - 请使用nixpkgs-unfree或用户配置。
创建二进制Overlay仓库
当nixpkgs构建的社区版本缺少功能时(常见于开源核心工具),创建一个获取官方二进制文件的overlay。
模式(参考0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay)
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
version = "1.0.0";
# 平台特定二进制文件
sources = {
"x86_64-linux" = {
url = "https://example.com/tool-linux-amd64-v${version}";
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
"aarch64-linux" = {
url = "https://example.com/tool-linux-arm64-v${version}";
sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
};
"x86_64-darwin" = {
url = "https://example.com/tool-darwin-amd64-v${version}";
sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=";
};
"aarch64-darwin" = {
url = "https://example.com/tool-darwin-arm64-v${version}";
sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=";
};
};
source = sources.${system} or (throw "不支持的系统: ${system}");
toolPackage = pkgs.stdenv.mkDerivation {
pname = "tool";
inherit version;
src = pkgs.fetchurl {
inherit (source) url sha256;
};
sourceRoot = ".";
dontUnpack = true;
installPhase = ''
mkdir -p $out/bin
cp $src $out/bin/tool
chmod +x $out/bin/tool
'';
meta = with pkgs.lib; {
description = "工具描述";
homepage = "https://example.com";
license = licenses.unfree; # 或适当的许可证
platforms = builtins.attrNames sources;
};
};
in {
packages.default = toolPackage;
packages.tool = toolPackage;
overlays.default = final: prev: {
tool = toolPackage;
};
})
// {
overlays.default = final: prev: {
tool = self.packages.${prev.system}.tool;
};
};
}
获取SHA256哈希值
nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0
# 返回base32格式的哈希,转换为SRI格式:
nix hash to-sri --type sha256 <base32-hash>
或直接使用SRI:
nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0
开发Shell模式
基础Shell
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
python3
];
shellHook = ''
echo "开发环境就绪"
'';
};
带环境变量
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ postgresql ];
# 在shell入口设置
DATABASE_URL = "postgres://localhost/dev";
# 或在shellHook中设置动态值
shellHook = ''
export PROJECT_ROOT="$(pwd)"
'';
};
原生依赖(C库)
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
openssl
postgresql
];
# 暴露头文件和库
shellHook = ''
export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH"
export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH"
export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
'';
};
Direnv集成
flake项目的.envrc:
use flake
对于没有nixpkgs-unfree的非自由软件包:
export NIXPKGS_ALLOW_UNFREE=1
use flake --impure
常用命令
# 更新所有输入
nix flake update
# 更新特定输入
nix flake update some-input
# 检查flake有效性
nix flake check
# 显示flake元数据
nix flake metadata
# 进入开发shell
nix develop
# 在开发shell中运行命令
nix develop -c <command>
# 构建包
nix build .#packageName
# 运行包
nix run .#packageName
故障排除
"unexpected argument"错误
所有输入必须在输出函数中列出:
# 错误
outputs = { self, nixpkgs }: ...
# 正确(如果有更多输入)
outputs = { self, nixpkgs, other-input, ... }: ...
使用nix develop时的非自由软件包错误
flake.nix中的config.allowUnfree不会传播到nix develop。使用:
- nixpkgs-unfree输入(推荐)
- 用户的
~/.config/nixpkgs/config.nix NIXPKGS_ALLOW_UNFREE=1 nix develop --impure
重复的Nixpkgs下载
使用follows将输入链式连接到单个nixpkgs源。
Overlay未应用
导入nixpkgs时确保overlay在overlays列表中:
pkgs = import nixpkgs {
inherit system;
overlays = [ my-overlay.overlays.default ];
};
哈希不匹配
使用nix-prefetch-url重新获取并更新哈希。当上游在同一URL更新二进制文件时,哈希会改变。