nixos-and-flakes-book/docs/nixpkgs/callpackage.md

202 lines
7.5 KiB
Markdown
Raw Normal View History

2023-07-04 06:18:46 +02:00
# `pkgs.callPackage`
`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-10-04 08:13:49 +02:00
## 1. Without `pkgs.callPackage`
2023-10-04 08:13:49 +02:00
We can define a Nix package using code like this:
2023-10-04 08:13:49 +02:00
```nix
pkgs.writeShellScriptBin "hello" ''echo "hello, ryan!"''
```
To verify this, you can use `nix repl`, and you'll see that the result is indeed a
Derivation:
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»
```
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
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
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
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`.
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
`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
# ......
```
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.
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
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;
})
```
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
});
# ......
}
```
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:
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)