如何从源码构建Nix包?default.nix编写逻辑技术问询
我太懂这种感觉了——明明能看懂Nix表达式的语法,可真要动手写一个能构建出软件包的default.nix,脑子里就一片空白,看着gedit那种现成的表达式,完全摸不着别人是怎么一步步凑出来的对吧?其实你没搞懂的不是Nix语言,而是把软件的常规构建流程映射到Nix表达式的逻辑,下面我就拆解几个核心要点:
1. 先搞懂软件本身的构建流程,这是基础
Nix不是凭空构建软件的,它本质上是把你在常规Linux环境下编译软件的步骤,用Nix的方式封装起来。比如gedit是GNOME旗下的编辑器,它的常规构建流程是:
- 用
meson setup生成构建文件(因为它用meson构建系统) - 用
ninja编译代码 - 用
meson install把产物安装到指定目录
你得先搞清楚目标软件用什么构建系统(meson、autotools、cmake还是其他)、需要哪些依赖(编译时的工具、运行时的库)、有没有自定义的配置参数——这些信息是写Nix表达式的前提。
2. 用好Nixpkgs的通用构建函数:stdenv.mkDerivation(或其封装)
绝大多数Nix软件包都是基于stdenv.mkDerivation来写的,它帮你处理了环境变量、路径隔离、依赖传递这些底层细节。以gedit为例,它用的是GNOME专属的封装gnome.mkDerivation(本质是对stdenv.mkDerivation的扩展,专门处理GNOME软件的共性需求,比如gsettings schema安装、图标处理)。
你需要重点关注这些核心参数:
src:指定软件源码的来源,比如gedit用fetchFromGitLab拉取GNOME仓库的源码,你需要填对仓库地址、commit哈希或者版本号。nativeBuildInputs:编译时需要的工具,比如meson、ninja、pkg-config、gettext——这些工具是用来生成构建文件、编译代码的,不需要打包到最终的软件包里。buildInputs:运行时依赖的库,比如gtk3、libadwaita、gtksourceview5——这些是gedit运行时必须链接的库,会被自动包含到最终的包中。mesonFlags(针对meson构建的软件):传递给meson setup的自定义参数,比如启用某个插件、关闭某个功能,比如gedit可能会加-Dplugin_markdown=true来启用markdown插件。
3. 参考Nixpkgs里的同类包,抄作业是最快的方法
Nixpkgs仓库里有几十万现成的包,找和你的目标软件同类的例子是最有效的学习方式。比如gedit是GNOME桌面应用,你可以去看pkgs/desktops/gnome/apps目录下的其他应用(比如gnome-text-editor、nautilus),它们的表达式结构几乎一致:
- 都用
gnome.mkDerivation - 依赖的编译工具大同小异(meson、ninja、pkg-config这些基本是标配)
- 运行时依赖都是GNOME生态的库(gtk、libadwaita等)
看别人怎么处理依赖、怎么传递构建参数,比自己瞎琢磨快多了。
4. 调试迭代:用nix-build和nix-shell排查问题
写表达式不可能一次就对,学会调试很重要:
- 用
nix-build -A gedit构建gedit,看终端输出的日志,如果报错说“找不到某个库”,就把对应的库加到buildInputs里;如果说“找不到某个命令”,就加到nativeBuildInputs里。 - 用
nix-shell -A gedit --pure进入一个纯净的构建环境,手动执行常规的构建步骤(比如meson setup build、ninja -C build),看哪里卡壳,这样能精准定位需要补充的依赖或参数。
举个简化版的gedit表达式例子
{ lib, stdenv, fetchFromGitLab, meson, ninja, pkg-config, gettext, gtk3, libadwaita, gtksourceview5 }: stdenv.mkDerivation rec { pname = "gedit"; version = "44.2"; src = fetchFromGitLab { domain = "gitlab.gnome.org"; owner = "GNOME"; repo = pname; rev = version; hash = "sha256-xxxxxxxxx"; # 替换成实际的哈希值 }; nativeBuildInputs = [ meson ninja pkg-config gettext ]; buildInputs = [ gtk3 libadwaita gtksourceview5 ]; mesonFlags = [ "-Dplugin_example=true" ]; meta = with lib; { description = "GNOME's text editor"; homepage = "https://wiki.gnome.org/Apps/Gedit"; license = licenses.gpl2Plus; platforms = platforms.linux; maintainers = teams.gnome.members; }; }
这个简化版里,我们用stdenv.mkDerivation定义了包的基本信息、源码、依赖和构建参数,和实际的gedit表达式核心逻辑是一致的。
说白了,写Nix包表达式的核心就是:把你手动编译软件的每一步,翻译成Nix能理解的参数和配置,再借助Nixpkgs的现有工具和同类例子,慢慢就能上手了。
内容的提问来源于stack exchange,提问作者ricky




