lib.packagesFromDirectoryRecursive: default to creating new scopes when newScope is provided

Co-authored-by: Rebecca Turner <rbt@sent.as>
This commit is contained in:
nicoo 2024-12-01 10:44:54 +00:00 committed by Paul Meyer
parent 41f219d1d9
commit 32235deeb8
2 changed files with 47 additions and 17 deletions

View file

@ -308,6 +308,7 @@ in
```
packagesFromDirectoryRecursive :: (args :: {
callPackage :: Path -> {} -> a,
newScope? :: AttrSet -> scope,
directory :: Path,
recurseIntoDirectory? :: (args -> AttrSet) -> args -> AttrSet,
...
@ -320,6 +321,12 @@ in
: The function used to convert a Nix file's path into a leaf of the attribute set.
It is typically the `callPackage` function, taken from either `pkgs` or a new scope corresponding to the `directory`.
`newScope`
: If present, this function is used by the default `recurseIntoDirectory` to generate a new scope.
The arguments are updated with the scope's `callPackage` and `newScope` functions, so packages can require
anything in their scope, or in an ancestor of their scope.
This argument has no effect when `recurseIntoDirectory` is provided.
`directory`
: The directory to read package files from.
@ -328,6 +335,20 @@ in
: Equivalently, this function takes `processDir` and `args`, and can modify arguments passed to `processDir`
(same as above) before calling it, as well as modify its output (which is then returned by `recurseIntoDirectory`).
:::{.note}
When `newScope` is set, the default `recurseIntoDirectory` is equivalent to:
```nix
processDir: { newScope, ... }@args:
# create a new scope and mark it `recurseForDerivations`
lib.recurseIntoAttrs (lib.makeScope newScope (self:
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
processDir (args // {
inherit (self) callPackage newScope;
})
))
```
:::
# Examples
:::{.example}
## Basic use of `lib.packagesFromDirectoryRecursive`
@ -349,12 +370,6 @@ in
```nix
packagesFromDirectoryRecursive {
inherit (pkgs) callPackage newScope;
recurseIntoDirectory = f: { newScope, ... }@args:
lib.recurseIntoAttrset (lib.makeScope newScope (self:
f (args // {
inherit (self) callPackage newScope;
})
));
directory = ./my-packages;
}
=> { ... }
@ -378,22 +393,43 @@ in
::::
*/
packagesFromDirectoryRecursive =
let
inherit (lib) concatMapAttrs id makeScope recurseIntoAttrs removeSuffix;
inherit (lib.path) append;
in
{
callPackage,
directory,
recurseIntoDirectory ? lib.id,
# recurseIntoDirectory can modify the function used when processing directory entries; see nixdoc above
recurseIntoDirectory ?
if args ? newScope then
# `processDir` is the same function as defined below,
# `args` are the arguments passed to (this recursive call of) `packagesFromDirectoryRecursive`
processDir: { newScope, ... }@args:
# Create a new scope and mark it `recurseForDerivations`.
# This lets the packages refer to each other.
# See:
# [lib.makeScope](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope) and
# [lib.recurseIntoAttrs](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope)
recurseIntoAttrs (makeScope newScope (self:
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
processDir (args // {
inherit (self) callPackage newScope;
})
))
else
# otherwise, no modification is necessary
id,
...
}@args:
let
inherit (lib) concatMapAttrs removeSuffix;
inherit (lib.path) append;
defaultPath = append directory "package.nix";
in
if pathExists defaultPath then
# if `${directory}/package.nix` exists, call it directly
callPackage defaultPath {}
else let
f = { callPackage, ... }@newArgs:
processDir = { callPackage, ... }@newArgs:
concatMapAttrs (name: type:
# otherwise, for each directory entry
let path = append directory name; in
@ -412,5 +448,5 @@ in
''
) (builtins.readDir directory);
in
recurseIntoDirectory f args;
recurseIntoDirectory processDir args;
}

View file

@ -2696,12 +2696,6 @@ runTests {
in {
expr = lib.filterAttrsRecursive (name: value: !lib.elem name [ "callPackage" "newScope" "overrideScope" "packages" ]) (packagesFromDirectoryRecursive {
inherit (emptyScope) callPackage newScope;
recurseIntoDirectory = f: { newScope, ... }@args:
recurseIntoAttrs (makeScope newScope (self:
f (args // {
inherit (self) callPackage newScope;
})
));
directory = ./packages-from-directory/scope;
});
expected = lib.recurseIntoAttrs {