2023-07-04 06:18:46 +02:00
|
|
|
|
# `pkgs.callPackage`
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
`pkgs.callPackage` is used to parameterize the construction of Nix Derivation. To
|
|
|
|
|
understand its purpose, let's first consider how we would define a Nix package (also known
|
|
|
|
|
as a Derivation) without using `pkgs.callPackage`.
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
## 1. Without `pkgs.callPackage`
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
We can define a Nix package using code like this:
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
```nix
|
|
|
|
|
pkgs.writeShellScriptBin "hello" ''echo "hello, ryan!"''
|
|
|
|
|
```
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
To verify this, you can use `nix repl`, and you'll see that the result is indeed a
|
|
|
|
|
Derivation:
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
```shell
|
|
|
|
|
› nix repl -f '<nixpkgs>'
|
|
|
|
|
Welcome to Nix 2.13.5. Type :? for help.
|
|
|
|
|
|
|
|
|
|
Loading installable ''...
|
|
|
|
|
Added 19203 variables.
|
|
|
|
|
|
|
|
|
|
nix-repl> pkgs.writeShellScriptBin "hello" '' echo "hello, xxx!" ''
|
|
|
|
|
«derivation /nix/store/zhgar12vfhbajbchj36vbbl3mg6762s8-hello.drv»
|
|
|
|
|
```
|
2023-06-23 16:19:10 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
While the definition of this Derivation is quite concise, most Derivations in nixpkgs are
|
|
|
|
|
much more complex. In previous sections, we introduced and extensively used the
|
|
|
|
|
`import xxx.nix` method to import Nix expressions from other Nix files, which can enhance
|
|
|
|
|
code maintainability.
|
2023-06-27 05:31:47 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
1. To enhance maintainability, you can store the definition of the Derivation in a
|
|
|
|
|
separate file, e.g., `hello.nix`.
|
|
|
|
|
1. However, the context within `hello.nix` itself doesn't include the `pkgs` variable,
|
|
|
|
|
so you'll need to modify its content to pass `pkgs` as a parameter to `hello.nix`.
|
|
|
|
|
2. In places where you need to use this Derivation, you can use `import ./hello.nix pkgs`
|
|
|
|
|
to import `hello.nix` and use `pkgs` as a parameter to execute the function defined
|
|
|
|
|
within.
|
2023-07-04 06:18:46 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
Let's continue to verify this using `nix repl`, and you'll see that the result is still a
|
|
|
|
|
Derivation:
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
|
|
|
|
```shell
|
2024-03-16 11:07:01 +01:00
|
|
|
|
› cat hello.nix
|
2023-10-04 08:13:49 +02:00
|
|
|
|
pkgs:
|
|
|
|
|
pkgs.writeShellScriptBin "hello" '' echo "hello, xxx!" ''
|
|
|
|
|
|
|
|
|
|
› nix repl -f '<nixpkgs>'
|
|
|
|
|
Welcome to Nix 2.13.5. Type :? for help.
|
|
|
|
|
|
|
|
|
|
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
|
|
|
|
|
Loading installable ''...
|
|
|
|
|
Added 19203 variables.
|
|
|
|
|
|
|
|
|
|
nix-repl> import ./hello.nix pkgs
|
|
|
|
|
«derivation /nix/store/zhgar12vfhbajbchj36vbbl3mg6762s8-hello.drv»
|
|
|
|
|
```
|
2023-07-04 06:18:46 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
## 2. Using `pkgs.callPackage`
|
2023-07-04 06:18:46 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
In the previous example without `pkgs.callPackage`, we directly passed `pkgs` as a
|
|
|
|
|
parameter to `hello.nix`. However, this approach has some drawbacks:
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
|
|
|
|
1. All other dependencies of the `hello` Derivation are tightly coupled with `pkgs`.
|
2024-03-16 12:29:05 +01:00
|
|
|
|
1. If we need custom dependencies, we have to modify either `pkgs` or the content of
|
|
|
|
|
`hello.nix`, which can be cumbersome.
|
|
|
|
|
2. In cases where `hello.nix` becomes complex, it's challenging to determine which
|
|
|
|
|
Derivations from `pkgs` it relies on, making it difficult to analyze the dependencies
|
|
|
|
|
between Derivations.
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
`pkgs.callPackage`, as a tool for parameterizing the construction of Derivations,
|
|
|
|
|
addresses these issues. Let's take a look at its source code and comments
|
|
|
|
|
[nixpkgs/lib/customisation.nix#L101-L121](https://github.com/NixOS/nixpkgs/blob/fe138d3/lib/customisation.nix#L101-L121):
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
2023-10-05 17:44:04 +02:00
|
|
|
|
```nix
|
|
|
|
|
/* Call the package function in the file `fn` with the required
|
|
|
|
|
arguments automatically. The function is called with the
|
|
|
|
|
arguments `args`, but any missing arguments are obtained from
|
|
|
|
|
`autoArgs`. This function is intended to be partially
|
|
|
|
|
parameterised, e.g.,
|
|
|
|
|
|
|
|
|
|
callPackage = callPackageWith pkgs;
|
|
|
|
|
pkgs = {
|
|
|
|
|
libfoo = callPackage ./foo.nix { };
|
|
|
|
|
libbar = callPackage ./bar.nix { };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
If the `libbar` function expects an argument named `libfoo`, it is
|
|
|
|
|
automatically passed as an argument. Overrides or missing
|
|
|
|
|
arguments can be supplied in `args`, e.g.
|
|
|
|
|
|
|
|
|
|
libbar = callPackage ./bar.nix {
|
|
|
|
|
libfoo = null;
|
|
|
|
|
enableX11 = true;
|
|
|
|
|
};
|
|
|
|
|
*/
|
|
|
|
|
callPackageWith = autoArgs: fn: args:
|
|
|
|
|
let
|
|
|
|
|
f = if lib.isFunction fn then fn else import fn;
|
|
|
|
|
fargs = lib.functionArgs f;
|
|
|
|
|
|
|
|
|
|
# All arguments that will be passed to the function
|
|
|
|
|
# This includes automatic ones and ones passed explicitly
|
2024-03-16 11:07:01 +01:00
|
|
|
|
allArgs = builtins.intersectAttrs fargs autoArgs // args;
|
2023-10-05 17:44:04 +02:00
|
|
|
|
|
|
|
|
|
# ......
|
|
|
|
|
```
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
In essence, `pkgs.callPackage` is used as `pkgs.callPackage fn args`, where the place
|
|
|
|
|
holder `fn` is a Nix file or function, and `args` is an attribute set. Here's how it
|
|
|
|
|
works:
|
|
|
|
|
|
|
|
|
|
1. `pkgs.callPackage fn args` first checks if `fn` is a function or a file. If it's a
|
|
|
|
|
file, it imports the function defined within.
|
|
|
|
|
1. After this step, you have a function, typically with parameters like `lib`,
|
|
|
|
|
`stdenv`, `fetchurl`, and possibly some custom parameters.
|
|
|
|
|
2. Next, `pkgs.callPackage fn args` merges `args` with the `pkgs` attribute set. If there
|
|
|
|
|
are conflicts, the parameters in `args` will override those in `pkgs`.
|
|
|
|
|
3. Then, `pkgs.callPackage fn args` extracts the parameters of the `fn` function from the
|
|
|
|
|
merged attribute set and uses them to execute the function.
|
2023-10-04 08:13:49 +02:00
|
|
|
|
4. The result of the function execution is a Derivation, which is a Nix package.
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
What can a Nix file or function, used as an argument to `pkgs.callPackage`, look like? You
|
|
|
|
|
can examine examples we've used before in
|
|
|
|
|
[Nixpkgs's Advanced Usage - Introduction](./intro.md): `hello.nix`, `fcitx5-rime.nix`,
|
|
|
|
|
`vscode/with-extensions.nix`, and `firefox/common.nix`. All of them can be imported using
|
|
|
|
|
`pkgs.callPackage`.
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
For instance, if you've defined a custom NixOS kernel configuration in `kernel.nix` and
|
|
|
|
|
made the development branch name and kernel source code configurable:
|
2023-06-27 05:31:47 +02:00
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
{
|
|
|
|
|
lib,
|
|
|
|
|
stdenv,
|
|
|
|
|
linuxManualConfig,
|
|
|
|
|
|
|
|
|
|
src,
|
|
|
|
|
boardName,
|
|
|
|
|
...
|
|
|
|
|
}:
|
|
|
|
|
(linuxManualConfig {
|
|
|
|
|
version = "5.10.113-thead-1520";
|
|
|
|
|
modDirVersion = "5.10.113";
|
|
|
|
|
|
|
|
|
|
inherit src lib stdenv;
|
2023-06-28 11:41:46 +02:00
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
# file path to the generated kernel config file(the `.config` generated by make menuconfig)
|
2023-06-27 05:31:47 +02:00
|
|
|
|
#
|
2023-10-04 08:13:49 +02:00
|
|
|
|
# here is a special usage to generate a file path from a string
|
2023-06-27 05:31:47 +02:00
|
|
|
|
configfile = ./. + "${boardName}_config";
|
|
|
|
|
|
|
|
|
|
allowImportFromDerivation = true;
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
You can use `pkgs.callPackage ./hello.nix {}` in any Nix module to import and use it,
|
|
|
|
|
replacing any of its parameters as needed:
|
2023-06-27 05:31:47 +02:00
|
|
|
|
|
|
|
|
|
```nix
|
2023-06-28 11:41:46 +02:00
|
|
|
|
{ lib, pkgs, pkgsKernel, kernel-src, ... }:
|
2023-06-27 05:31:47 +02:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
# ......
|
|
|
|
|
|
|
|
|
|
boot = {
|
|
|
|
|
# ......
|
|
|
|
|
kernelPackages = pkgs.linuxPackagesFor (pkgs.callPackage ./pkgs/kernel {
|
2023-10-04 08:13:49 +02:00
|
|
|
|
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.
|
2023-06-27 05:31:47 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
# ......
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
As shown above, by using `pkgs.callPackage`, you can pass different `src` and `boardName`
|
|
|
|
|
to the function defined in `kernel.nix`, to generate different kernel packages. This
|
|
|
|
|
allows you to adapt the same `kernel.nix` to different kernel source code and development
|
|
|
|
|
boards.
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
|
|
|
|
The advantages of `pkgs.callPackage` are:
|
|
|
|
|
|
2024-03-16 12:29:05 +01:00
|
|
|
|
1. Derivation definitions are parameterized, and all dependencies of the Derivation are
|
|
|
|
|
the function parameters in its definition. This makes it easy to analyze dependencies
|
|
|
|
|
between Derivations.
|
|
|
|
|
2. All dependencies and other custom parameters of the Derivation can be easily replaced
|
|
|
|
|
by using the second parameter of `pkgs.callPackage`, greatly enhancing Derivation
|
|
|
|
|
reusability.
|
|
|
|
|
3. While achieving the above two functionalities, it does not increase code complexity, as
|
|
|
|
|
all dependencies in `pkgs` can be automatically injected.
|
2023-10-04 08:13:49 +02:00
|
|
|
|
|
|
|
|
|
So it's always recommended to use `pkgs.callPackage` to define Derivations.
|
2023-06-27 05:31:47 +02:00
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
2023-10-04 08:13:49 +02:00
|
|
|
|
- [Chapter 13. Callpackage Design Pattern - Nix Pills](https://nixos.org/guides/nix-pills/callpackage-design-pattern.html)
|
|
|
|
|
- [callPackage, a tool for the lazy - The Summer of Nix](https://summer.nixos.org/blog/callpackage-a-tool-for-the-lazy/)
|
|
|
|
|
- [Document what callPackage does and its preconditions - Nixpkgs Issues](https://github.com/NixOS/nixpkgs/issues/36354)
|