I personally struggled with these functions for a while, especially pkgs.callPackage
, until understanding them (at least I hope I did). To spare my fellow newbies, hopefully this patch will help.
output of git format-patch HEAD^
From e5a6d070ee45eee4edd3522bb13cb899faa58a25 Mon Sep 17 00:00:00 2001
From: Do Nix <um2iai+9g233t397b57k@sharklasers.com>
Date: Fri, 14 Feb 2025 20:25:39 +0100
Subject: [PATCH] Add/modify documentation for pkgs.callPackage and
lib.callPackageWith
These functions are used quite often nixpkgs and hopefully these changes
will make it easier for newbies to understand what they do and how they
are used.
---
lib/customisation.nix | 86 +++++++++++++++++++++++----------------
pkgs/top-level/splice.nix | 52 +++++++++++++++++++++--
2 files changed, 99 insertions(+), 39 deletions(-)
diff --git a/lib/customisation.nix b/lib/customisation.nix
index 9c50e6a26c3d..f50244a0bff1 100644
--- a/lib/customisation.nix
+++ b/lib/customisation.nix
@@ -189,62 +189,76 @@ rec {
);
/**
- 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.,
+ Call the function (or the function within the file) `funcOrPath` with `defaultArgs`,
+ which can be overriden by `args`. This function is intended to be partially
+ parameterised / curried (see examples).
+ # Inputs (positional)
+
+ 1. `defaultArgs` - `AttrSet`
+
+ Default
+
+ 2. `funcOrPath` - `((AttrSet -> a) | Path)`
+
+ Either a function that is expected to build a package from the given attribute set
+ or a file that returns such a function when imported.
+
+ 3. `args` - `AttrSet`
+
+ Arguments that will be passed to the function. These are merged with `defaultArgs`.
+ Attributes in `args` override or extend those of `defaultArgs`.
+
+ It is quite common to pass an empty attribute set if the `defaultArgs` suffice.
+
+ # Type
+
+ ```
+ callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
+ ```
+
+ # Examples
+
+ ## `pkgs.callPackage`
+
+ The most common use for this is the `pkgs.callPackage` function that `pkgs`
+ as the `defaultArgs`. See the documentation of that function for examples.
+
+ ## Contrived
+
+ Create a `callPackage` function which will call whichever function (or file with a function)
+ passed to it with `libfoo` and `libbar` in the function arguments.
+
+ :::{.example}
```nix
- callPackage = callPackageWith pkgs;
pkgs = {
libfoo = callPackage ./foo.nix { };
libbar = callPackage ./bar.nix { };
};
+ callPackage = callPackageWith pkgs;
```
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.
+ :::{.example}
```nix
libbar = callPackage ./bar.nix {
libfoo = null;
enableX11 = true;
};
```
-
- <!-- TODO: Apply "Example:" tag to the examples above -->
-
- # Inputs
-
- `autoArgs`
-
- : 1\. Function argument
-
- `fn`
-
- : 2\. Function argument
-
- `args`
-
- : 3\. Function argument
-
- # Type
-
- ```
- callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
- ```
*/
callPackageWith =
- autoArgs: fn: args:
+ defaultArgs: funcOrPath: args:
let
- f = if isFunction fn then fn else import fn;
- fargs = functionArgs f;
+ func = if isFunction funcOrPath then funcOrPath else import funcOrPath;
+ fargs = functionArgs func;
# All arguments that will be passed to the function
# This includes automatic ones and ones passed explicitly
- allArgs = intersectAttrs fargs autoArgs // args;
+ allArgs = intersectAttrs fargs defaultArgs // args;
# a list of argument names that the function requires, but
# wouldn't be passed to it
@@ -259,7 +273,7 @@ rec {
# Get a list of suggested argument names for a given missing one
getSuggestions =
arg:
- pipe (autoArgs // args) [
+ pipe (defaultArgs // args) [
attrNames
# Only use ones that are at most 2 edits away. While mork would work,
# levenshteinAtMost is only fast for 2 or less.
@@ -290,8 +304,8 @@ rec {
loc' =
if loc != null then
loc.file + ":" + toString loc.line
- else if !isFunction fn then
- toString fn + optionalString (pathIsDirectory fn) "/default.nix"
+ else if !isFunction funcOrPath then
+ toString funcOrPath + optionalString (pathIsDirectory funcOrPath) "/default.nix"
else
"<unknown location>";
in
@@ -303,7 +317,7 @@ rec {
in
if missingArgs == { } then
- makeOverridable f allArgs
+ makeOverridable func allArgs
# This needs to be an abort so it can't be caught with `builtins.tryEval`,
# which is used by nix-env and ofborg to filter out packages that don't evaluate.
# This way we're forced to fix such errors in Nixpkgs,
diff --git a/pkgs/top-level/splice.nix b/pkgs/top-level/splice.nix
index 3b600c22f60e..6a5b1c004953 100644
--- a/pkgs/top-level/splice.nix
+++ b/pkgs/top-level/splice.nix
@@ -142,9 +142,55 @@ in
{
inherit splicePackages;
- # We use `callPackage' to be able to omit function arguments that can be
- # obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
- # `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
+ /**
+ We use `callPackage' to be able to omit function arguments that can be
+ obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
+ `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
+
+ # Inputs (positional)
+
+
+ 1. `funcOrPath` - `((AttrSet -> a) | Path)`
+
+ Either a function that is expected to build a package from the given attribute set
+ or a file that returns such a function when imported.
+
+ 2. `args` - `AttrSet`
+
+ Arguments that will be passed to the function, overriding or extending attributes of `pkgs`.
+
+ # Examples
+
+ Define a function that uses attributes from `pkgs`
+
+ :::{.example}
+ **say-hello.nix**
+ ```nix
+ {
+ # Attributes found in `pkgs`
+ hello, lib, writeScript,
+ # An additional attribute not in `pkgs`
+ name,
+ ...
+ }:
+
+ writeScript "say-hello-${name}" ''
+ export PATH=${lib.strings.makeBinPath [hello]}
+ hello -g "Hello ${name}!"
+ ''
+ ```
+
+ In another file, call `say-hello.nix` it without worrying about passing all the arguments found in `pkgs`.
+ `name` must be passed though as it is not in the `pkgs` attribute set.
+
+ ```nix
+ { pkgs, ...}:
+ {
+ sayHelloPackage = pkgs.callPackage ./say-hello.nix { name = "Medina"; };
+ }
+ ```
+
+ */
callPackage = pkgs.newScope { };
callPackages = lib.callPackagesWith pkgsForCall;
--
2.47.2