diff options
author | Alyssa Ross <hi@alyssa.is> | 2024-03-01 11:40:12 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2024-03-01 11:40:12 +0100 |
commit | bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86 (patch) | |
tree | 8eb035cbab19794f6415cc460fac7226f7a58afc /nixpkgs/lib/fileset | |
parent | 66f707d69f1e423db5a35c2fe43b32781125a9af (diff) | |
parent | 09c1497ce5d4ed4a0edfdd44450d3048074cb300 (diff) | |
download | nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.gz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.bz2 nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.lz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.xz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.zst nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.zip |
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/lib/fileset')
-rw-r--r-- | nixpkgs/lib/fileset/default.nix | 37 | ||||
-rw-r--r-- | nixpkgs/lib/fileset/internal.nix | 23 | ||||
-rwxr-xr-x | nixpkgs/lib/fileset/tests.sh | 124 |
3 files changed, 134 insertions, 50 deletions
diff --git a/nixpkgs/lib/fileset/default.nix b/nixpkgs/lib/fileset/default.nix index c007b60def0a..ce9afc796a3f 100644 --- a/nixpkgs/lib/fileset/default.nix +++ b/nixpkgs/lib/fileset/default.nix @@ -23,6 +23,10 @@ Add files in file sets to the store to use as derivation sources. + - [`lib.fileset.toList`](#function-library-lib.fileset.toList): + + The list of files contained in a file set. + Combinators: - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions): @@ -102,6 +106,7 @@ let _coerceMany _toSourceFilter _fromSourceFilter + _toList _unionMany _fileFilter _printFileset @@ -412,6 +417,38 @@ in { filter = sourceFilter; }; + + /* + The list of file paths contained in the given file set. + + :::{.note} + This function is strict in the entire file set. + This is in contrast with combinators [`lib.fileset.union`](#function-library-lib.fileset.union), + [`lib.fileset.intersection`](#function-library-lib.fileset.intersection) and [`lib.fileset.difference`](#function-library-lib.fileset.difference). + + Thus it is recommended to call `toList` on file sets created using the combinators, + instead of doing list processing on the result of `toList`. + ::: + + The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions). + + Type: + toList :: FileSet -> [ Path ] + + Example: + toList ./. + [ ./README.md ./Makefile ./src/main.c ./src/main.h ] + + toList (difference ./. ./src) + [ ./README.md ./Makefile ] + */ + toList = + # The file set whose file paths to return. + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + fileset: + _toList (_coerce "lib.fileset.toList: Argument" fileset); + /* The file set containing all files that are in either of two given file sets. This is the same as [`unions`](#function-library-lib.fileset.unions), diff --git a/nixpkgs/lib/fileset/internal.nix b/nixpkgs/lib/fileset/internal.nix index f4fcc83e1012..0d97ef174568 100644 --- a/nixpkgs/lib/fileset/internal.nix +++ b/nixpkgs/lib/fileset/internal.nix @@ -18,6 +18,7 @@ let attrNames attrValues mapAttrs + mapAttrsToList optionalAttrs zipAttrsWith ; @@ -29,6 +30,7 @@ let inherit (lib.lists) all commonPrefix + concatLists elemAt filter findFirst @@ -539,6 +541,27 @@ rec { ${baseNameOf root} = rootPathType; }; + # Turns a file set into the list of file paths it includes. + # Type: fileset -> [ Path ] + _toList = fileset: + let + recurse = path: tree: + if isAttrs tree then + concatLists (mapAttrsToList (name: value: + recurse (path + "/${name}") value + ) tree) + else if tree == "directory" then + recurse path (readDir path) + else if tree == null then + [ ] + else + [ path ]; + in + if fileset._internalIsEmptyWithoutBase then + [ ] + else + recurse fileset._internalBase fileset._internalTree; + # Transforms the filesetTree of a file set to a shorter base path, e.g. # _shortenTreeBase [ "foo" ] (_create /foo/bar null) # => { bar = null; } diff --git a/nixpkgs/lib/fileset/tests.sh b/nixpkgs/lib/fileset/tests.sh index af8338eb7855..405fa04d8e06 100755 --- a/nixpkgs/lib/fileset/tests.sh +++ b/nixpkgs/lib/fileset/tests.sh @@ -275,7 +275,6 @@ createTree() { # ) # checkFileset './a' # Pass the fileset as the argument checkFileset() { - # New subshell so that we can have a separate trap handler, see `trap` below local fileset=$1 # Create the tree @@ -283,16 +282,20 @@ checkFileset() { # Process the tree into separate arrays for included paths, excluded paths and excluded files. local -a included=() + local -a includedFiles=() local -a excluded=() local -a excludedFiles=() for p in "${!tree[@]}"; do case "${tree[$p]}" in 1) included+=("$p") + # If keys end with a `/` we treat them as directories, otherwise files + if [[ ! "$p" =~ /$ ]]; then + includedFiles+=("$p") + fi ;; 0) excluded+=("$p") - # If keys end with a `/` we treat them as directories, otherwise files if [[ ! "$p" =~ /$ ]]; then excludedFiles+=("$p") fi @@ -302,6 +305,10 @@ checkFileset() { esac done + # Test that lib.fileset.toList contains exactly the included files. + # The /#/./ part prefixes each element with `./` + expectEqual "toList ($fileset)" "sort lessThan [ ${includedFiles[*]/#/./} ]" + expression="toSource { root = ./.; fileset = $fileset; }" # We don't have lambda's in bash unfortunately, @@ -338,13 +345,17 @@ checkFileset() { #### Error messages ##### +# We're using [[:blank:]] here instead of \s, because only the former is POSIX +# (see https://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html#tag_007_003_005). +# And indeed, Darwin's bash only supports the former + # Absolute paths in strings cannot be passed as `root` expectFailure 'toSource { root = "/nix/store/foobar"; fileset = ./.; }' 'lib.fileset.toSource: `root` \(/nix/store/foobar\) is a string-like value, but it should be a path instead. -\s*Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.' +[[:blank:]]*Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.' expectFailure 'toSource { root = cleanSourceWith { src = ./.; }; fileset = ./.; }' 'lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead. -\s*To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`. -\s*Note that this only works for sources created from paths.' +[[:blank:]]*To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`. +[[:blank:]]*Note that this only works for sources created from paths.' # Only paths are accepted as `root` expectFailure 'toSource { root = 10; fileset = ./.; }' 'lib.fileset.toSource: `root` is of type int, but it should be a path instead.' @@ -354,9 +365,9 @@ mkdir -p {foo,bar}/mock-root expectFailure 'with ((import <nixpkgs/lib>).extend (import <nixpkgs/lib/fileset/mock-splitRoot.nix>)).fileset; toSource { root = ./foo/mock-root; fileset = ./bar/mock-root; } ' 'lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` \('"$work"'/foo/mock-root\): -\s*`root`: Filesystem root is "'"$work"'/foo/mock-root" -\s*`fileset`: Filesystem root is "'"$work"'/bar/mock-root" -\s*Different filesystem roots are not supported.' +[[:blank:]]*`root`: Filesystem root is "'"$work"'/foo/mock-root" +[[:blank:]]*`fileset`: Filesystem root is "'"$work"'/bar/mock-root" +[[:blank:]]*Different filesystem roots are not supported.' rm -rf -- * # `root` needs to exist @@ -365,8 +376,8 @@ expectFailure 'toSource { root = ./a; fileset = ./.; }' 'lib.fileset.toSource: ` # `root` needs to be a file touch a expectFailure 'toSource { root = ./a; fileset = ./a; }' 'lib.fileset.toSource: `root` \('"$work"'/a\) is a file, but it should be a directory instead. Potential solutions: -\s*- If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function. -\s*- If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as '"$work"', and set `fileset` to the file path.' +[[:blank:]]*- If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function. +[[:blank:]]*- If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as '"$work"', and set `fileset` to the file path.' rm -rf -- * # The fileset argument should be evaluated, even if the directory is empty @@ -375,36 +386,36 @@ expectFailure 'toSource { root = ./.; fileset = abort "This should be evaluated" # Only paths under `root` should be able to influence the result mkdir a expectFailure 'toSource { root = ./a; fileset = ./.; }' 'lib.fileset.toSource: `fileset` could contain files in '"$work"', which is not under the `root` \('"$work"'/a\). Potential solutions: -\s*- Set `root` to '"$work"' or any directory higher up. This changes the layout of the resulting store path. -\s*- Set `fileset` to a file set that cannot contain files outside the `root` \('"$work"'/a\). This could change the files included in the result.' +[[:blank:]]*- Set `root` to '"$work"' or any directory higher up. This changes the layout of the resulting store path. +[[:blank:]]*- Set `fileset` to a file set that cannot contain files outside the `root` \('"$work"'/a\). This could change the files included in the result.' rm -rf -- * # non-regular and non-symlink files cannot be added to the Nix store mkfifo a expectFailure 'toSource { root = ./.; fileset = ./a; }' 'lib.fileset.toSource: `fileset` contains a file that cannot be added to the store: '"$work"'/a -\s*This file is neither a regular file nor a symlink, the only file types supported by the Nix store. -\s*Therefore the file set cannot be added to the Nix store as is. Make sure to not include that file to avoid this error.' +[[:blank:]]*This file is neither a regular file nor a symlink, the only file types supported by the Nix store. +[[:blank:]]*Therefore the file set cannot be added to the Nix store as is. Make sure to not include that file to avoid this error.' rm -rf -- * # Path coercion only works for paths expectFailure 'toSource { root = ./.; fileset = 10; }' 'lib.fileset.toSource: `fileset` is of type int, but it should be a file set or a path instead.' expectFailure 'toSource { root = ./.; fileset = "/some/path"; }' 'lib.fileset.toSource: `fileset` \("/some/path"\) is a string-like value, but it should be a file set or a path instead. -\s*Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.' +[[:blank:]]*Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.' expectFailure 'toSource { root = ./.; fileset = cleanSourceWith { src = ./.; }; }' 'lib.fileset.toSource: `fileset` is a `lib.sources`-based value, but it should be a file set or a path instead. -\s*To convert a `lib.sources`-based value to a file set you can use `lib.fileset.fromSource`. -\s*Note that this only works for sources created from paths.' +[[:blank:]]*To convert a `lib.sources`-based value to a file set you can use `lib.fileset.fromSource`. +[[:blank:]]*Note that this only works for sources created from paths.' # Path coercion errors for non-existent paths expectFailure 'toSource { root = ./.; fileset = ./a; }' 'lib.fileset.toSource: `fileset` \('"$work"'/a\) is a path that does not exist. -\s*To create a file set from a path that may not exist, use `lib.fileset.maybeMissing`.' +[[:blank:]]*To create a file set from a path that may not exist, use `lib.fileset.maybeMissing`.' # File sets cannot be evaluated directly expectFailure 'union ./. ./.' 'lib.fileset: Directly evaluating a file set is not supported. -\s*To turn it into a usable source, use `lib.fileset.toSource`. -\s*To pretty-print the contents, use `lib.fileset.trace` or `lib.fileset.traceVal`.' +[[:blank:]]*To turn it into a usable source, use `lib.fileset.toSource`. +[[:blank:]]*To pretty-print the contents, use `lib.fileset.trace` or `lib.fileset.traceVal`.' expectFailure '_emptyWithoutBase' 'lib.fileset: Directly evaluating a file set is not supported. -\s*To turn it into a usable source, use `lib.fileset.toSource`. -\s*To pretty-print the contents, use `lib.fileset.trace` or `lib.fileset.traceVal`.' +[[:blank:]]*To turn it into a usable source, use `lib.fileset.toSource`. +[[:blank:]]*To pretty-print the contents, use `lib.fileset.trace` or `lib.fileset.traceVal`.' # Past versions of the internal representation are supported expectEqual '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 0; _internalBase = ./.; }' \ @@ -416,9 +427,9 @@ expectEqual '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 2; # Future versions of the internal representation are unsupported expectFailure '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 4; }' '<tests>: value is a file set created from a future version of the file set library with a different internal representation: -\s*- Internal version of the file set: 4 -\s*- Internal version of the library: 3 -\s*Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.' +[[:blank:]]*- Internal version of the file set: 4 +[[:blank:]]*- Internal version of the library: 3 +[[:blank:]]*Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.' # _create followed by _coerce should give the inputs back without any validation expectEqual '{ @@ -511,6 +522,19 @@ expectEqual '_toSourceFilter (_create /. { foo = "regular"; }) "/foo" ""' 'true' expectEqual '_toSourceFilter (_create /. { foo = null; }) "/foo" ""' 'false' +## lib.fileset.toList +# This function is mainly tested in checkFileset + +# The error context for an invalid argument must be correct +expectFailure 'toList null' 'lib.fileset.toList: Argument is of type null, but it should be a file set or a path instead.' + +# Works for the empty fileset +expectEqual 'toList _emptyWithoutBase' '[ ]' + +# Works on empty paths +expectEqual 'toList ./.' '[ ]' + + ## lib.fileset.union, lib.fileset.unions @@ -519,16 +543,16 @@ mkdir -p {foo,bar}/mock-root expectFailure 'with ((import <nixpkgs/lib>).extend (import <nixpkgs/lib/fileset/mock-splitRoot.nix>)).fileset; toSource { root = ./.; fileset = union ./foo/mock-root ./bar/mock-root; } ' 'lib.fileset.union: Filesystem roots are not the same: -\s*First argument: Filesystem root is "'"$work"'/foo/mock-root" -\s*Second argument: Filesystem root is "'"$work"'/bar/mock-root" -\s*Different filesystem roots are not supported.' +[[:blank:]]*First argument: Filesystem root is "'"$work"'/foo/mock-root" +[[:blank:]]*Second argument: Filesystem root is "'"$work"'/bar/mock-root" +[[:blank:]]*Different filesystem roots are not supported.' expectFailure 'with ((import <nixpkgs/lib>).extend (import <nixpkgs/lib/fileset/mock-splitRoot.nix>)).fileset; toSource { root = ./.; fileset = unions [ ./foo/mock-root ./bar/mock-root ]; } ' 'lib.fileset.unions: Filesystem roots are not the same: -\s*Element 0: Filesystem root is "'"$work"'/foo/mock-root" -\s*Element 1: Filesystem root is "'"$work"'/bar/mock-root" -\s*Different filesystem roots are not supported.' +[[:blank:]]*Element 0: Filesystem root is "'"$work"'/foo/mock-root" +[[:blank:]]*Element 1: Filesystem root is "'"$work"'/bar/mock-root" +[[:blank:]]*Different filesystem roots are not supported.' rm -rf -- * # Coercion errors show the correct context @@ -632,9 +656,9 @@ mkdir -p {foo,bar}/mock-root expectFailure 'with ((import <nixpkgs/lib>).extend (import <nixpkgs/lib/fileset/mock-splitRoot.nix>)).fileset; toSource { root = ./.; fileset = intersection ./foo/mock-root ./bar/mock-root; } ' 'lib.fileset.intersection: Filesystem roots are not the same: -\s*First argument: Filesystem root is "'"$work"'/foo/mock-root" -\s*Second argument: Filesystem root is "'"$work"'/bar/mock-root" -\s*Different filesystem roots are not supported.' +[[:blank:]]*First argument: Filesystem root is "'"$work"'/foo/mock-root" +[[:blank:]]*Second argument: Filesystem root is "'"$work"'/bar/mock-root" +[[:blank:]]*Different filesystem roots are not supported.' rm -rf -- * # Coercion errors show the correct context @@ -741,8 +765,8 @@ rm -rf -- * # Also not the other way around mkdir a expectFailure 'toSource { root = ./a; fileset = difference ./. ./a; }' 'lib.fileset.toSource: `fileset` could contain files in '"$work"', which is not under the `root` \('"$work"'/a\). Potential solutions: -\s*- Set `root` to '"$work"' or any directory higher up. This changes the layout of the resulting store path. -\s*- Set `fileset` to a file set that cannot contain files outside the `root` \('"$work"'/a\). This could change the files included in the result.' +[[:blank:]]*- Set `root` to '"$work"' or any directory higher up. This changes the layout of the resulting store path. +[[:blank:]]*- Set `fileset` to a file set that cannot contain files outside the `root` \('"$work"'/a\). This could change the files included in the result.' rm -rf -- * # Difference actually works @@ -819,7 +843,7 @@ expectFailure 'fileFilter null (abort "this is not needed")' 'lib.fileset.fileFi # The second argument needs to be an existing path expectFailure 'fileFilter (file: abort "this is not needed") _emptyWithoutBase' 'lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead. -\s*If you need to filter files in a file set, use `intersection fileset \(fileFilter pred \./\.\)` instead.' +[[:blank:]]*If you need to filter files in a file set, use `intersection fileset \(fileFilter pred \./\.\)` instead.' expectFailure 'fileFilter (file: abort "this is not needed") null' 'lib.fileset.fileFilter: Second argument is of type null, but it should be 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.' @@ -1083,7 +1107,7 @@ rm -rf -- * # String-like values are not supported expectFailure 'fromSource (lib.cleanSource "")' 'lib.fileset.fromSource: The source origin of the argument is a string-like value \(""\), but it should be a path instead. -\s*Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.' +[[:blank:]]*Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.' # Wrong type expectFailure 'fromSource null' 'lib.fileset.fromSource: The source origin of the argument is of type null, but it should be a path instead.' @@ -1400,10 +1424,10 @@ expectEqual '(import '"$storePath"' { fs = lib.fileset; }).outPath' \""$storePat ## But it fails if the path is imported with a fetcher that doesn't remove .git (like just using "${./.}") expectFailure 'import "${./.}" { fs = lib.fileset; }' 'lib.fileset.gitTracked: The argument \(.*\) is a store path within a working tree of a Git repository. -\s*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. -\s*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. -\s*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. -\s*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' +[[:blank:]]*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. +[[:blank:]]*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. +[[:blank:]]*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. +[[:blank:]]*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' ## Even with submodules if [[ -n "$fetchGitSupportsSubmodules" ]]; then @@ -1427,15 +1451,15 @@ if [[ -n "$fetchGitSupportsSubmodules" ]]; then ## But it fails if the path is imported with a fetcher that doesn't remove .git (like just using "${./.}") expectFailure 'import "${./.}" { fs = lib.fileset; }' 'lib.fileset.gitTrackedWith: The second argument \(.*\) is a store path within a working tree of a Git repository. - \s*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. - \s*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. - \s*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. - \s*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' + [[:blank:]]*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. + [[:blank:]]*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. + [[:blank:]]*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. + [[:blank:]]*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' expectFailure 'import "${./.}/sub" { fs = lib.fileset; }' 'lib.fileset.gitTracked: The argument \(.*/sub\) is a store path within a working tree of a Git repository. - \s*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. - \s*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. - \s*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. - \s*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' + [[:blank:]]*This indicates that a source directory was imported into the store using a method such as `import "\$\{./.\}"` or `path:.`. + [[:blank:]]*This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. + [[:blank:]]*You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. + [[:blank:]]*If you can'\''t avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.' fi rm -rf -- * |