Commit 2035f8a3 authored by Silvan Mosberger's avatar Silvan Mosberger
Browse files

lib.fileset.fileFilter: Don't run predicate unnecessarily

Before:

    nix-repl> fileset.trace (fileset.fileFilter (file: builtins.trace file.name false) ./default.nix)
    trace: README.md
    trace: benchmark.sh
    trace: default.nix
    trace: internal.nix
    trace: mock-splitRoot.nix
    trace: tests.sh

After:

    nix-repl> fileset.trace (fileset.fileFilter (file: builtins.trace file.name false) ./default.nix)
    trace: default.nix
parent e1d83317
Loading
Loading
Loading
Loading
+23 −16
Original line number Diff line number Diff line
@@ -732,29 +732,36 @@ rec {
  # Type: ({ name, type, ... } -> Bool) -> FileSet -> FileSet
  _fileFilter = predicate: fileset:
    let
      # Check the predicate for a path and a filesetTree, returning a new filesetTree
      # Type: Path -> filesetTree -> filesetTree
      recurse = path: tree:
        mapAttrs (name: subtree:
          if isAttrs subtree || subtree == "directory" then
            recurse (path + "/${name}") subtree
          else if
      # Check the predicate for a single file
      # Type: String -> String -> filesetTree
      fromFile = name: type:
        if
          predicate {
              inherit name;
              type = subtree;
            inherit name type;
            # To ensure forwards compatibility with more arguments being added in the future,
            # adding an attribute which can't be deconstructed :)
            "lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file }:`, use `{ name, file, ... }:` instead." = null;
          }
        then
            subtree
          type
        else
          null;

      # Check the predicate for all files in a directory
      # Type: Path -> filesetTree
      fromDir = path: tree:
        mapAttrs (name: subtree:
          if isAttrs subtree || subtree == "directory" then
            fromDir (path + "/${name}") subtree
          else if subtree == null then
            null
          else
            fromFile name subtree
        ) (_directoryEntries path tree);
    in
    if fileset._internalIsEmptyWithoutBase then
      _emptyWithoutBase
    else
      _create fileset._internalBase
        (recurse fileset._internalBase fileset._internalTree);
        (fromDir fileset._internalBase fileset._internalTree);
}
+20 −0
Original line number Diff line number Diff line
@@ -857,6 +857,26 @@ checkFileset 'union ./c/a (fileFilter (file: assert file.name != "a"; true) ./.)
# but here we need to use ./c
checkFileset 'union (fileFilter (file: assert file.name != "a"; true) ./.) ./c'

# Also lazy, the filter isn't called on a filtered out path
tree=(
    [a]=1
    [b]=0
    [c]=0
)
checkFileset 'fileFilter (file: assert file.name != "c"; file.name == "a") (difference ./. ./c)'

# Make sure single files are filtered correctly
tree=(
    [a]=1
    [b]=0
)
checkFileset 'fileFilter (file: assert file.name == "a"; true) ./a'
tree=(
    [a]=0
    [b]=0
)
checkFileset 'fileFilter (file: assert file.name == "a"; false) ./a'

## Tracing

# The second trace argument is returned