about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/README.md20
-rw-r--r--lib/customisation.nix18
-rw-r--r--lib/default.nix2
-rw-r--r--lib/fileset/README.md3
-rw-r--r--lib/fileset/default.nix6
-rw-r--r--lib/fileset/internal.nix39
-rwxr-xr-xlib/fileset/tests.sh27
-rw-r--r--lib/trivial.nix34
8 files changed, 120 insertions, 29 deletions
diff --git a/lib/README.md b/lib/README.md
index 627086843db7c..220940bc21224 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -74,3 +74,23 @@ path/tests/prop.sh
 # Run the lib.fileset tests
 fileset/tests.sh
 ```
+
+## Commit conventions
+
+- Make sure you read about the [commit conventions](../CONTRIBUTING.md#commit-conventions) common to Nixpkgs as a whole.
+
+- Format the commit messages in the following way:
+
+  ```
+  lib.(section): (init | add additional argument | refactor | etc)
+
+  (Motivation for change. Additional information.)
+  ```
+
+  Examples:
+
+  * lib.getExe': check arguments
+  * lib.fileset: Add an additional argument in the design docs
+
+    Closes #264537
+
diff --git a/lib/customisation.nix b/lib/customisation.nix
index c7d40339d05f5..08fc5db0614de 100644
--- a/lib/customisation.nix
+++ b/lib/customisation.nix
@@ -76,19 +76,22 @@ rec {
      Type:
        makeOverridable :: (AttrSet -> a) -> AttrSet -> a
   */
-  makeOverridable = f: lib.setFunctionArgs
-    (origArgs: let
+  makeOverridable = f:
+    let
+      # Creates a functor with the same arguments as f
+      mirrorArgs = lib.mirrorFunctionArgs f;
+    in
+    mirrorArgs (origArgs:
+    let
       result = f origArgs;
 
-      # Creates a functor with the same arguments as f
-      copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f);
       # Changes the original arguments with (potentially a function that returns) a set of new attributes
       overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
 
       # Re-call the function but with different arguments
-      overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs));
+      overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs));
       # Change the result of the function call by applying g to it
-      overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
+      overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
     in
       if builtins.isAttrs result then
         result // {
@@ -102,8 +105,7 @@ rec {
         lib.setFunctionArgs result (lib.functionArgs result) // {
           override = overrideArgs;
         }
-      else result)
-    (lib.functionArgs f);
+      else result);
 
 
   /* Call the package function in the file `fn` with the required
diff --git a/lib/default.nix b/lib/default.nix
index 80fc17985879a..a2958e561cf32 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -74,7 +74,7 @@ let
       importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
       info showWarnings nixpkgsVersion version isInOldestRelease
       mod compare splitByAndCompare
-      functionArgs setFunctionArgs isFunction toFunction
+      functionArgs setFunctionArgs isFunction toFunction mirrorFunctionArgs
       toHexString toBaseDigits inPureEvalMode;
     inherit (self.fixedPoints) fix fix' converge extends composeExtensions
       composeManyExtensions makeExtensible makeExtensibleWithCustomName;
diff --git a/lib/fileset/README.md b/lib/fileset/README.md
index 8518d88a7d642..91f892a1be951 100644
--- a/lib/fileset/README.md
+++ b/lib/fileset/README.md
@@ -241,7 +241,4 @@ Arguments:
 ## To update in the future
 
 Here's a list of places in the library that need to be updated in the future:
-- > The file set library is currently somewhat limited but is being expanded to include more functions over time.
-
-  in [the manual](../../doc/functions/fileset.section.md)
 - If/Once a function exists that can optionally include a path depending on whether it exists, the error message for the path not existing in `_coerce` should mention the new function
diff --git a/lib/fileset/default.nix b/lib/fileset/default.nix
index 372d445269f55..d90c770633d83 100644
--- a/lib/fileset/default.nix
+++ b/lib/fileset/default.nix
@@ -188,7 +188,7 @@ in {
             - Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path.
             - Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.''
     else
-      builtins.seq sourceFilter
+      seq sourceFilter
       cleanSourceWith {
         name = "source";
         src = root;
@@ -380,7 +380,7 @@ in {
       fileFilter (file: hasPrefix "." file.name) ./.
 
       # Include all regular files (not symlinks or others) in the current directory
-      fileFilter (file: file.type == "regular")
+      fileFilter (file: file.type == "regular") ./.
   */
   fileFilter =
     /*
@@ -401,7 +401,7 @@ in {
     fileset:
     if ! isFunction predicate then
       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
       _fileFilter predicate
         (_coerce "lib.fileset.fileFilter: Second argument" fileset);
diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix
index 717253f45715b..b245caade1f53 100644
--- a/lib/fileset/internal.nix
+++ b/lib/fileset/internal.nix
@@ -786,29 +786,40 @@ rec {
         _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)
       ) (_directoryEntries path lhs);
 
+  # Filters all files in a file set based on a predicate
+  # Type: ({ name, type, ... } -> Bool) -> FileSet -> FileSet
   _fileFilter = predicate: fileset:
     let
-      recurse = path: tree:
+      # Check the predicate for a single file
+      # Type: String -> String -> filesetTree
+      fromFile = name: type:
+        if
+          predicate {
+            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
+          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
-            recurse (path + "/${name}") subtree
-          else if
-            predicate {
-              inherit name;
-              type = subtree;
-              # 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
-          else
+            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);
 }
diff --git a/lib/fileset/tests.sh b/lib/fileset/tests.sh
index 796a03b52f0e4..5ef155d25a881 100755
--- a/lib/fileset/tests.sh
+++ b/lib/fileset/tests.sh
@@ -810,6 +810,13 @@ checkFileset 'difference ./. ./b'
 
 ## 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
 tree=()
 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
 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
diff --git a/lib/trivial.nix b/lib/trivial.nix
index c23fc6070be46..a89c1aa25b1f7 100644
--- a/lib/trivial.nix
+++ b/lib/trivial.nix
@@ -449,6 +449,40 @@ rec {
     (f ? __functor && isFunction (f.__functor f));
 
   /*
+    `mirrorFunctionArgs f g` creates a new function `g'` with the same behavior as `g` (`g' x == g x`)
+    but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`).
+
+    Type:
+      mirrorFunctionArgs :: (a -> b) -> (a -> c) -> (a -> c)
+
+    Example:
+      addab = {a, b}: a + b
+      addab { a = 2; b = 4; }
+      => 6
+      lib.functionArgs addab
+      => { a = false; b = false; }
+      addab1 = attrs: addab attrs + 1
+      addab1 { a = 2; b = 4; }
+      => 7
+      lib.functionArgs addab1
+      => { }
+      addab1' = lib.mirrorFunctionArgs addab addab1
+      addab1' { a = 2; b = 4; }
+      => 7
+      lib.functionArgs addab1'
+      => { a = false; b = false; }
+  */
+  mirrorFunctionArgs =
+    # Function to provide the argument metadata
+    f:
+    let
+      fArgs = functionArgs f;
+    in
+    # Function to set the argument metadata to
+    g:
+    setFunctionArgs g fArgs;
+
+  /*
     Turns any non-callable values into constant functions.
     Returns callable values as is.