Merge pull request #267381 from tweag/fileset.fileFilter-path

`fileset.fileFilter`: Don't run predicate unnecessarily
This commit is contained in:
Silvan Mosberger 2023-11-15 01:19:36 +01:00 committed by GitHub
commit 7e533bab6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 16 deletions

View file

@ -380,7 +380,7 @@ in {
fileFilter (file: hasPrefix "." file.name) ./. fileFilter (file: hasPrefix "." file.name) ./.
# Include all regular files (not symlinks or others) in the current directory # Include all regular files (not symlinks or others) in the current directory
fileFilter (file: file.type == "regular") fileFilter (file: file.type == "regular") ./.
*/ */
fileFilter = fileFilter =
/* /*
@ -401,7 +401,7 @@ in {
fileset: fileset:
if ! isFunction predicate then if ! isFunction predicate then
throw '' throw ''
lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function.'' lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
else else
_fileFilter predicate _fileFilter predicate
(_coerce "lib.fileset.fileFilter: Second argument" fileset); (_coerce "lib.fileset.fileFilter: Second argument" fileset);

View file

@ -786,29 +786,40 @@ rec {
_differenceTree (path + "/${name}") lhsValue (rhs.${name} or null) _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)
) (_directoryEntries path lhs); ) (_directoryEntries path lhs);
# Filters all files in a file set based on a predicate
# Type: ({ name, type, ... } -> Bool) -> FileSet -> FileSet
_fileFilter = predicate: fileset: _fileFilter = predicate: fileset:
let let
recurse = path: tree: # Check the predicate for a single file
mapAttrs (name: subtree: # Type: String -> String -> filesetTree
if isAttrs subtree || subtree == "directory" then fromFile = name: type:
recurse (path + "/${name}") subtree if
else if
predicate { predicate {
inherit name; inherit name type;
type = subtree;
# To ensure forwards compatibility with more arguments being added in the future, # To ensure forwards compatibility with more arguments being added in the future,
# adding an attribute which can't be deconstructed :) # 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; "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 then
subtree type
else 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 null
else
fromFile name subtree
) (_directoryEntries path tree); ) (_directoryEntries path tree);
in in
if fileset._internalIsEmptyWithoutBase then if fileset._internalIsEmptyWithoutBase then
_emptyWithoutBase _emptyWithoutBase
else else
_create fileset._internalBase _create fileset._internalBase
(recurse fileset._internalBase fileset._internalTree); (fromDir fileset._internalBase fileset._internalTree);
} }

View file

@ -810,6 +810,13 @@ checkFileset 'difference ./. ./b'
## File filter ## File filter
# The first argument needs to be a function
expectFailure 'fileFilter null (abort "this is not needed")' 'lib.fileset.fileFilter: First argument is of type null, but it should be a function instead.'
# The second argument can be a file set or an existing path
expectFailure 'fileFilter (file: abort "this is not needed") null' 'lib.fileset.fileFilter: Second argument is of type null, but it should be a file set or a path instead.'
expectFailure 'fileFilter (file: abort "this is not needed") ./a' 'lib.fileset.fileFilter: Second argument \('"$work"'/a\) is a path that does not exist.'
# The predicate is not called when there's no files # The predicate is not called when there's no files
tree=() tree=()
checkFileset 'fileFilter (file: abort "this is not needed") ./.' checkFileset 'fileFilter (file: abort "this is not needed") ./.'
@ -875,6 +882,26 @@ checkFileset 'union ./c/a (fileFilter (file: assert file.name != "a"; true) ./.)
# but here we need to use ./c # but here we need to use ./c
checkFileset 'union (fileFilter (file: assert file.name != "a"; true) ./.) ./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 ## Tracing
# The second trace argument is returned # The second trace argument is returned