Skip to content

Nix 软件打包入门

WIP 未完成,目前请移步如下参考文档学习 Nix 打包。

参考文档


一些草稿

1. stdenv 构建介绍

TODO

2. language specific frameworks

TODO

Fetchers

构建输入除了直接来自文件系统路径之外,还可以通过 Fetchers 来获取,Fetcher 是一种特殊的函数,它的输入是一个 attribute set,输出是 Nix Store 中的一个系统路径。

Nix 提供了四个内置的 Fetcher,分别是:

  • builtins.fetchurl:从 url 中下载文件
  • builtins.fetchTarball:从 url 中下载 tarball 文件
  • builtins.fetchGit:从 git 仓库中下载文件
  • builtins.fetchClosure:从 Nix Store 中获取 Derivation

举例:

nix
builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
# result example => "/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"

builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
# result example(auto unzip the tarball) => "/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"

Derivations

官方 Nixpkgs 包仓库中的软件包已经能满足绝大部分用户的使用,在学习 NixOS 的前期不太需要深入了解 Derivation 的使用细节,有个印象就行。 本书会在后面 Nix 软件打包入门 中详细介绍相关内容,这里仅做简要介绍。

Derivation 描述了如何构建一个软件包,是一个软件包构建流程的 Nix 语言描述,它声明了构建时需要有哪些依赖项、需要什么构建工具链、要设置哪些环境变量、哪些构建参数、先干啥后干啥等等。

Derivation 的构建结果是一个 Store Object,其中包含了软件包的所有二进制程序、配置文件等等内容。 Store Object 的存放路径格式为 /nix/store/<hash>-<name>,其中 <hash> 是构建结果的 hash 值,<name> 是它的名字。路径 hash 值确保了每个构建结果都是唯一的,因此可以多版本共存,而且不会出现依赖冲突的问题。

/nix/store 是一个特殊的文件路径,它被称为 Store,存放所有的 Store Objects,这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。

Derivation 实质上只是一个 attribute set,Nix 底层会使用内置函数 builtins.derivation 将这个 attribute set 构建为一个 Store Object。 我们实际编写 Derivation 时,通常使用的是 stdenv.mkDerivation,它是前述内置函数 builtins.derivation 的 Nix 语言 wrapper,屏蔽了底层的细节,简化了用法。

一个简单的 Derivation 如下,它声明了一个名为 hello 的应用程序(摘抄自 nixpkgs/pkgs/hello):

nix
{ callPackage
, lib
, stdenv
, fetchurl
, nixos
, testers
, hello
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "hello";
  version = "2.12.1";

  src = fetchurl {
    url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz";
    sha256 = "sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA=";
  };

  doCheck = true;

  passthru.tests = {
    version = testers.testVersion { package = hello; };

    invariant-under-noXlibs =
      testers.testEqualDerivation
        "hello must not be rebuilt when environment.noXlibs is set."
        hello
        (nixos { environment.noXlibs = true; }).pkgs.hello;
  };

  passthru.tests.run = callPackage ./test.nix { hello = finalAttrs.finalPackage; };

  meta = with lib; {
    description = "A program that produces a familiar, friendly greeting";
    longDescription = ''
      GNU Hello is a program that prints "Hello, world!" when you run it.
      It is fully customizable.
    '';
    homepage = "https://www.gnu.org/software/hello/manual/";
    changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${finalAttrs.version}";
    license = licenses.gpl3Plus;
    maintainers = [ maintainers.eelco ];
    platforms = platforms.all;
  };
})