Skip to content
On this page

pkgs.callPackage

In the previous section, we used import xxx.nix to import Nix files multiple times. This syntax simply returns the execution result of the file without any further processing.

pkgs.callPackage is also used to import Nix files. Its syntax is pkgs.callPackage xxx.nix { ... }. However, unlike import, the Nix file imported by it must be a Derivation or a function that returns a Derivation. Its result is also a Derivation (a software package).

So, what does the Nix file that can be used as a parameter of pkgs.callPackge look like? You can take a look at the hello.nix, fcitx5-rime.nix, vscode/with-extensions.nix, and firefox/common.nix files we mentioned earlier. They can all be imported by pkgs.callPackage.

When the xxx.nix used in pkgs.callPackge xxx.nix {...} is a function (most Nix packages are like this), the execution flow is as follows:

  1. pkgs.callPackge xxx.nix {...} first imports xxx.nix to get the function defined in it. The parameters of this function usually have lib, stdenv, fetchurl, and other parameters, as well as some custom parameters that usually have default values.
  2. Then, pkgs.callPackge looks up the value matching the name from the current environment as the parameter to be passed to the function. Parameters like lib, stdenv, and fetchurl are defined in nixpkgs and will be found in this step.
  3. Next, pkgs.callPackge merges its second parameter {...} with the attribute set obtained in the previous step and passes it to the function imported from xxx.nix to execute it.
  4. Finally, we get a Derivation as the result of the function execution.

The common usage of pkgs.callPackage is to import customized Nix packages and use them in Nix Modules.

For example, suppose we have customized a NixOS kernel configuration kernel.nix, which uses the SBC's name and kernel source as its variable parameters:

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;
})

Then we can use pkgs.callPackage ./kernel.nix {} in any Nix Module to import and replace any of its parameters:

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.
    });

  # ......
}

Similarly to the example above, we can use pkgs.callPackage to pass different src and boardName parameters to the function defined in kernel.nix. This allows us to generate different kernel packages. By changing the parameters passed to it, kernel.nix can be used to adapt to different kernel sources and development boards.

References