Skip to content

lib.fileset should have a way to filter directories #271307

@infinisil

Description

@infinisil

Problem

Sometimes one needs to filter out directories with a specific name throughout the source directory.

For example, you might have a structure like

- foo
  - target
- bar
  - baz
    - target
  - qux
    - target

And you want to remove all of the target directories.

Currently, the lib.fileset library does not have any function to make this work. To do that currently you need to work around it with either:

  • lib.fileset.fromSource (lib.sources.cleanSourceWith {
      src = ./.
      filter = pathString: type:
        # untested..
        type != "directory" || baseNameOf pathString != "target";
    }
  • Some recursive readDir and lib.fileset.unions I don't want to write out

Requirements

  1. It must not recurse into filtered-out directories for performance reasons
  2. It must be intuitive to use

Potential solutions

Directory name predicate

directoryFilter (dir: dir.name == "target") ./.

This creates a file set containing all files within directories named "target". E.g. ./foo/target/some/file.txt would be included because one of foo, target and some satisfies the predicate. Does it satisfy the requirements?

  1. ✔️ It doesn't need to recurse into the filtered out directories, but the selection needs to be inverted to actually filter out the ones with target:

    difference ./. (directoryFilter (dir:
      dir.name == "target"
    ) ./.)
  2. ❌ This is not intuitive, because one might expect an inverted predicate to return the complement set:

    directoryFilter (dir:
      dir.name != "target"
    ) ./.

    But this doesn't work: This would include ./foo/target/some/file.txt, since foo satisfies the predicate.

Give the fileFilter predicate access to the file's directories

Like already described in #269504. This would allow filtering certain directories:

fileFilter (file:
  # Only include files whose directory components don't have `target`
  ! elem "target" file.dirComponents
) ./.

This way ./foo/target/some/file.nix would not be included, because target is in ./foo/target/some. Does it satisfy the requirements?

  1. ❌ No, this does have to recurse for every file, because the predicate needs files. So this would be very slow.
  2. ✔️ Yes this is intuitive. One can also invert the predicate and get the expected result.

Does a solution satisfying both requirements exist?

I don't know yet

This issue is sponsored by Antithesis

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.status: stalehttps://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md6.topic: libThe Nixpkgs function library
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions