Skip to content
On this page

pkgs.callPackage

前面我们介绍并大量使用了 import xxx.nix 来导入 Nix 文件,这种语法只是单纯地返回该文件的执行结果,不会对该结果进行进一步处理。 比如说 xxx.nix 的内容是形如 {...}: {...},那么 import xxx.nix 的结果就是该文件中定义的这个函数。

pkgs.callPackage 也是用来导入 Nix 文件的,它的语法是 pkgs.callPackage xxx.nix { ... }. 但跟 import 不同的是,它导入的 nix 文件内容必须是一个 Derivation 或者返回 Derivation 的函数,它的执行结果一定是一个 Derivation,也就是一个软件包。

那可以作为 pkgs.callPackge 参数的 nix 文件具体长啥样呢,可以去看看我们前面举例过的 hello.nix fcitx5-rime.nix vscode/with-extensions.nix firefox/common.nix,它们都可以被 pkgs.callPackage 导入。

pkgs.callPackge xxx.nix {...} 中的 xxx.nix,其内容为一个函数时(绝大多数 nix 包都是如此),执行流程如下:

  1. pkgs.callPackge xxx.nix {...} 会先 import xxx.nix,得到其中定义的函数,该函数的参数通常会有 lib, stdenv, fetchurl 等参数,以及一些自定义参数,自定义参数通常都有默认值。
  2. 接着 pkgs.callPackge 会首先从当前环境中查找名称匹配的值,作为将要传递给前述函数的参数。像 lib stdenv fetchurl 这些都是 nixpkgs 中的函数,在这一步就会查找到它们。
  3. 接着 pkgs.callPackge 会将其第二个参数 {...} 与前一步得到的参数集(attribute set)进行合并,得到一个新的参数列表,然后将其传递给该函数并执行。
  4. 函数执行结果是一个 Derivation,也就是一个软件包。

这个函数比较常见的用途是用来导入一些自定义的 Nix 包。

比如说我们自定义了一个 NixOS 内核配置 kernel.nix,并且将开发版名称与内核源码作为了可变更参数:

nix
{
  lib,
  stdenv,
  linuxManualConfig,

  src,
  boardName,
  ...
}:
(linuxManualConfig {
  version = "5.10.113-thead-1520";
  modDirVersion = "5.10.113";

  inherit src lib stdenv;
  
  # file path to the generated kernel config file(the `.config` generated by make menuconfig)
  #
  # here is a special usage to generate a file path from a string
  configfile = ./. + "${boardName}_config";

  allowImportFromDerivation = true;
})

那么就可以在任意 Nix Module 中使用 pkgs.callPackage ./hello.nix {} 来导入并使用它,并且替换它的任意参数。

nix
{ lib, pkgs, pkgsKernel, kernel-src, ... }: 

{
  # ......

  boot = {
    # ......
    kernelPackages = pkgs.linuxPackagesFor (pkgs.callPackage ./pkgs/kernel {
        src = kernel-src;  # kernel source is passed as a `specialArgs` and injected into this module.
        boardName = "licheepi4a";  # the board name, used to generate the kernel config file path.
    });

  # ......
}

就如上面所展示的,通过 pkgs.callPackage 我们可以给 kernel.nix 定义的函数传入不同的 srcboardName,来生成不同的内核包,这样就可以使用同一份 kernel.nix 来适配不同的内核源码与不同的开发板了。

参考