diff options
author | Silvan Mosberger <silvan.mosberger@tweag.io> | 2023-09-21 01:00:59 +0200 |
---|---|---|
committer | Silvan Mosberger <silvan.mosberger@tweag.io> | 2023-10-04 16:28:55 +0200 |
commit | efbcf5938fca42f004e34c3f56f9ab1a12a95559 (patch) | |
tree | 3a4e9c86eb023d033e170ec83c7c52ef17f96d45 /lib/fileset | |
parent | 86802e19ceffbdff6a2a80dbec2d5f14f9f40efa (diff) | |
download | nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.gz nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.bz2 nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.lz nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.xz nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.zst nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.zip |
lib.fileset: Add internal helpers for pretty-printing
Diffstat (limited to 'lib/fileset')
-rw-r--r-- | lib/fileset/internal.nix | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix index ad10b9ac0bb..ebfe69b010d 100644 --- a/lib/fileset/internal.nix +++ b/lib/fileset/internal.nix @@ -7,11 +7,14 @@ let isString pathExists readDir - typeOf + seq split + trace + typeOf ; inherit (lib.attrsets) + attrNames attrValues mapAttrs setAttrByPath @@ -241,7 +244,7 @@ rec { // value; /* - A normalisation of a filesetTree suitable for use in builtins.path-based filtering: + A normalisation of a filesetTree suitable filtering with `builtins.path`: - Replace all directories that have no files with `null`. This removes directories that would be empty - Replace all directories with all files with `"directory"`. @@ -270,6 +273,108 @@ rec { else tree; + /* + A minimal normalisation of a filesetTree, intended for pretty-printing: + - If all children of a path are recursively included or empty directories, the path itself is also recursively included + - If all children of a path are fully excluded or empty directories, the path itself is an empty directory + - Other empty directories are represented with the special "emptyDir" string + While these could be replaced with `null`, that would take another mapAttrs + + Note that this function is partially lazy. + + Type: Path -> filesetTree -> filesetTree (with "emptyDir"'s) + */ + _normaliseTreeMinimal = path: tree: + if tree == "directory" || isAttrs tree then + let + entries = _directoryEntries path tree; + normalisedSubtrees = mapAttrs (name: _normaliseTreeMinimal (path + "/${name}")) entries; + subtreeValues = attrValues normalisedSubtrees; + in + # If there are no entries, or all entries are empty directories, return "emptyDir". + # After this branch we know that there's at least one file + if all (value: value == "emptyDir") subtreeValues then + "emptyDir" + + # If all subtrees are fully included or empty directories + # (both of which are coincidentally represented as strings), return "directory". + # This takes advantage of the fact that empty directories can be represented as included directories. + # Note that the tree == "directory" check allows avoiding recursion + else if tree == "directory" || all (value: isString value) subtreeValues then + "directory" + + # If all subtrees are fully excluded or empty directories, return null. + # This takes advantage of the fact that empty directories can be represented as excluded directories + else if all (value: isNull value || value == "emptyDir") subtreeValues then + null + + # Mix of included and excluded entries + else + normalisedSubtrees + else + tree; + + # Trace a filesetTree in a pretty way when the resulting value is evaluated. + # This can handle both normal filesetTree's, and ones returned from _normaliseTreeMinimal + # Type: Path -> filesetTree (with "emptyDir"'s) -> Null + _printMinimalTree = base: tree: + let + treeSuffix = tree: + if isAttrs tree then + "" + else if tree == "directory" then + " (all files in directory)" + else + # This does "leak" the file type strings of the internal representation, + # but this is the main reason these file type strings even are in the representation! + # TODO: Consider removing that information from the internal representation for performance. + # The file types can still be printed by querying them only during tracing + " (${tree})"; + + # Only for attribute set trees + traceTreeAttrs = prevLine: indent: tree: + foldl' (prevLine: name: + let + subtree = tree.${name}; + + # Evaluating this prints the line for this subtree + thisLine = + trace "${indent}- ${name}${treeSuffix subtree}" prevLine; + in + if subtree == null || subtree == "emptyDir" then + # Don't print anything at all if this subtree is empty + prevLine + else if isAttrs subtree then + # A directory with explicit entries + # Do print this node, but also recurse + traceTreeAttrs thisLine "${indent} " subtree + else + # Either a file, or a recursively included directory + # Do print this node but no further recursion needed + thisLine + ) prevLine (attrNames tree); + + # Evaluating this will print the first line + firstLine = + if tree == null || tree == "emptyDir" then + trace "(empty)" null + else + trace "${toString base}${treeSuffix tree}" null; + in + if isAttrs tree then + traceTreeAttrs firstLine "" tree + else + firstLine; + + # Pretty-print a file set in a pretty way when the resulting value is evaluated + # Type: fileset -> Null + _printFileset = fileset: + if fileset._internalIsEmptyWithoutBase then + trace "(empty)" null + else + _printMinimalTree fileset._internalBase + (_normaliseTreeMinimal fileset._internalBase fileset._internalTree); + # Turn a fileset into a source filter function suitable for `builtins.path` # Only directories recursively containing at least one files are recursed into # Type: Path -> fileset -> (String -> String -> Bool) |