From d7f60bcb043925670c02a8ccf9067e97d647bc87 Mon Sep 17 00:00:00 2001 From: sterni Date: Wed, 15 Sep 2021 13:22:54 +0200 Subject: 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 --- nix/readTree/default.nix | 51 +++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 18 deletions(-) (limited to 'nix') diff --git a/nix/readTree/default.nix b/nix/readTree/default.nix index 5443d2edf5..68988424fb 100644 --- a/nix/readTree/default.nix +++ b/nix/readTree/default.nix @@ -47,26 +47,24 @@ let value = children.${name}; }) names); - # Create a mark containing the location of this attribute. - marker = parts: { + # Create a mark containing the location of this attribute and + # a list of all child attribute names added by readTree. + marker = parts: children: { __readTree = parts; + __readTreeChildren = builtins.attrNames children; }; - # The marker is added to every set that was imported directly by - # readTree. - importWithMark = args: scopedArgs: path: parts: filter: + # Import a file and enforce our calling convention + importFile = args: scopedArgs: path: parts: filter: let importedFile = if scopedArgs != {} then builtins.scopedImport scopedArgs path else import path; pathType = builtins.typeOf importedFile; - imported = - if pathType != "lambda" - then builtins.throw "readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }" - else importedFile (filter (argsWithPath args parts) parts); - in if (isAttrs imported) - then imported // (marker parts) - else imported; + in + if pathType != "lambda" + then builtins.throw "readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }" + else importedFile (filter (argsWithPath args parts) parts); nixFileName = file: let res = match "(.*)\\.nix" file; @@ -79,7 +77,7 @@ let self = if rootDir 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 # `.skip-subtree` file exists which makes readTree ignore the @@ -102,13 +100,30 @@ let # Import Nix files 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; - value = importWithMark args scopedArgs p (parts ++ [ c ]) argsFilter; + value = + if isAttrs imported + then imported // marker parts {} + else imported; }) nixFiles; - in if dir ? "default.nix" - then (if isAttrs self then self // (listToAttrs children) else self) - else (listToAttrs (nixChildren ++ children) // (marker parts)); + + nodeValue = if dir ? "default.nix" then self else {}; + + allChildren = listToAttrs ( + if dir ? "default.nix" + then children + else nixChildren ++ children + ); + + in + if isAttrs nodeValue + then nodeValue // allChildren // (marker parts allChildren) + else nodeValue; in { __functor = _: -- cgit 1.4.1