Skip to content

pkgs.callPackage

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

Another method to import Nix files is pkgs.callPackage. Its syntax is pkgs.callPackage xxx.nix { ... }. However, unlike import, the Nix file imported by pkgs.callPackage must be a Derivation or a function that returns a Derivation. The result of pkgs.callPackage is also a Derivation, which represents a software package.

So, what does a Nix file that can be used as a parameter for pkgs.callPackage look like? You can refer to the hello.nix, fcitx5-rime.nix, vscode/with-extensions.nix, and firefox/common.nix files mentioned earlier. All of these files can be imported using pkgs.callPackage.

When the xxx.nix file used in pkgs.callPackage xxx.nix {...} is a function (most Nix packages follow this pattern), the execution flow is as follows:

  1. pkgs.callPackage xxx.nix {...} first imports xxx.nix to obtain the function defined within it. This function usually has parameters like lib, stdenv, fetchurl, and sometimes additional custom parameters with default values.

  2. Then, pkgs.callPackage searches for a value matching the parameter names from the current environment. Parameters like lib, stdenv, and fetchurl are defined in nixpkgs and will be found in this step.

  3. Next, pkgs.callPackage merges its second parameter, {...}, with the attribute set obtained in the previous step. It then passes this merged set as the parameter to the function imported from xxx.nix for execution.

  4. Finally, the result of the function execution is a Derivation.

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

For example, let's say we have a customized NixOS kernel configuration file named 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 (`.config` generated by make menuconfig)
  #
  # Here, we use a special usage to generate a file path from a string.
  configfile = ./. + "${boardName}_config";

  allowImportFromDerivation = true;
})

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

  # ......
}

In the example above, we 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 pkgs.callPackage, kernel.nix can adapt to different kernel sources and development boards.

References

Released under the MIT License.