feat(nix/readTree): record list of children added by readTree

This change adds a new attribute to readTree nodes, `__readTreeChildren`
which is a list of attribute names added to this node by readTree.

This is then used by `gather` for `ci.targets` to avoid evaluating
attributes unnecessarily. Especially since Nix is not as lazy as we'd
like when determining types (i. e. child ? __readTree needs to force
`child` even when it's not an attribute set), evaluating attributes
unnecessarily is sometimes problematic.

Change-Id: I0a98691d41f987e23ee7e9ba21fbe465da5fe402
This commit is contained in:
sterni 2021-09-15 13:22:54 +02:00
parent d904724adf
commit d7f60bcb04
2 changed files with 35 additions and 19 deletions

View file

@ -79,13 +79,14 @@ let
# Include the node itself if it is eligible. # Include the node itself if it is eligible.
(if eligible node then [ node ] else []) (if eligible node then [ node ] else [])
# Include eligible children of the node # Include eligible children of the node
++ concatMap gather (attrValues node) ++ concatMap gather (map (attr: node."${attr}") node.__readTreeChildren)
# Include specified sub-targets of the node # Include specified sub-targets of the node
++ filter eligible (map ++ filter eligible (map
(k: (node."${k}" or {}) // { (k: (node."${k}" or {}) // {
# Keep the same tree location, but explicitly mark this # Keep the same tree location, but explicitly mark this
# node as a subtarget. # node as a subtarget.
__readTree = node.__readTree; __readTree = node.__readTree;
__readTreeChildren = [];
__subtarget = k; __subtarget = k;
}) })
(node.meta.targets or [])) (node.meta.targets or []))

View file

@ -47,26 +47,24 @@ let
value = children.${name}; value = children.${name};
}) names); }) names);
# Create a mark containing the location of this attribute. # Create a mark containing the location of this attribute and
marker = parts: { # a list of all child attribute names added by readTree.
marker = parts: children: {
__readTree = parts; __readTree = parts;
__readTreeChildren = builtins.attrNames children;
}; };
# The marker is added to every set that was imported directly by # Import a file and enforce our calling convention
# readTree. importFile = args: scopedArgs: path: parts: filter:
importWithMark = args: scopedArgs: path: parts: filter:
let let
importedFile = if scopedArgs != {} importedFile = if scopedArgs != {}
then builtins.scopedImport scopedArgs path then builtins.scopedImport scopedArgs path
else import path; else import path;
pathType = builtins.typeOf importedFile; pathType = builtins.typeOf importedFile;
imported = in
if pathType != "lambda" if pathType != "lambda"
then builtins.throw "readTree: trying to import ${toString path}, but its a ${pathType}, you need to make it a function like { depot, pkgs, ... }" then builtins.throw "readTree: trying to import ${toString path}, but its a ${pathType}, you need to make it a function like { depot, pkgs, ... }"
else importedFile (filter (argsWithPath args parts) parts); else importedFile (filter (argsWithPath args parts) parts);
in if (isAttrs imported)
then imported // (marker parts)
else imported;
nixFileName = file: nixFileName = file:
let res = match "(.*)\\.nix" file; let res = match "(.*)\\.nix" file;
@ -79,7 +77,7 @@ let
self = if rootDir self = if rootDir
then { __readTree = []; } then { __readTree = []; }
else importWithMark args scopedArgs initPath parts argsFilter; else importFile args scopedArgs initPath parts argsFilter;
# Import subdirectories of the current one, unless the special # Import subdirectories of the current one, unless the special
# `.skip-subtree` file exists which makes readTree ignore the # `.skip-subtree` file exists which makes readTree ignore the
@ -102,13 +100,30 @@ let
# Import Nix files # Import Nix files
nixFiles = filter (f: f != null) (map nixFileName (attrNames dir)); nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
nixChildren = map (c: let p = joinChild (c + ".nix"); in { nixChildren = map (c: let
p = joinChild (c + ".nix");
childParts = parts ++ [ c ];
imported = importFile args scopedArgs p childParts argsFilter;
in {
name = c; name = c;
value = importWithMark args scopedArgs p (parts ++ [ c ]) argsFilter; value =
if isAttrs imported
then imported // marker parts {}
else imported;
}) nixFiles; }) nixFiles;
in if dir ? "default.nix"
then (if isAttrs self then self // (listToAttrs children) else self) nodeValue = if dir ? "default.nix" then self else {};
else (listToAttrs (nixChildren ++ children) // (marker parts));
allChildren = listToAttrs (
if dir ? "default.nix"
then children
else nixChildren ++ children
);
in
if isAttrs nodeValue
then nodeValue // allChildren // (marker parts allChildren)
else nodeValue;
in { in {
__functor = _: __functor = _: