From d7f60bcb043925670c02a8ccf9067e97d647bc87 Mon Sep 17 00:00:00 2001 From: sterni Date: Wed, 15 Sep 2021 13:22:54 +0200 Subject: [PATCH] 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 --- default.nix | 3 ++- nix/readTree/default.nix | 51 ++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/default.nix b/default.nix index 866b3fa6b..941110b22 100644 --- a/default.nix +++ b/default.nix @@ -79,13 +79,14 @@ let # Include the node itself if it is eligible. (if eligible node then [ node ] else []) # 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 ++ filter eligible (map (k: (node."${k}" or {}) // { # Keep the same tree location, but explicitly mark this # node as a subtarget. __readTree = node.__readTree; + __readTreeChildren = []; __subtarget = k; }) (node.meta.targets or [])) diff --git a/nix/readTree/default.nix b/nix/readTree/default.nix index 5443d2edf..68988424f 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 = _: