diff options
Diffstat (limited to 'nixpkgs/lib/fileset/default.nix')
-rw-r--r-- | nixpkgs/lib/fileset/default.nix | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/nixpkgs/lib/fileset/default.nix b/nixpkgs/lib/fileset/default.nix new file mode 100644 index 000000000000..c007b60def0a --- /dev/null +++ b/nixpkgs/lib/fileset/default.nix @@ -0,0 +1,823 @@ +/* + <!-- This anchor is here for backwards compatibity --> + []{#sec-fileset} + + The [`lib.fileset`](#sec-functions-library-fileset) library allows you to work with _file sets_. + A file set is a (mathematical) set of local files that can be added to the Nix store for use in Nix derivations. + File sets are easy and safe to use, providing obvious and composable semantics with good error messages to prevent mistakes. + + ## Overview {#sec-fileset-overview} + + Basics: + - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion) + + - [`lib.fileset.maybeMissing`](#function-library-lib.fileset.maybeMissing): + + Create a file set from a path that may be missing. + + - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace): + + Pretty-print file sets for debugging. + + - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource): + + Add files in file sets to the store to use as derivation sources. + + Combinators: + - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions): + + Create a larger file set from all the files in multiple file sets. + + - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection): + + Create a smaller file set from only the files in both file sets. + + - [`lib.fileset.difference`](#function-library-lib.fileset.difference): + + Create a smaller file set containing all files that are in one file set, but not another one. + + Filtering: + - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter): + + Create a file set from all files that satisisfy a predicate in a directory. + + Utilities: + - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource): + + Create a file set from a `lib.sources`-based value. + + - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith): + + Create a file set from all tracked files in a local Git repository. + + If you need more file set functions, + see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it. + + + ## Implicit coercion from paths to file sets {#sec-fileset-path-coercion} + + All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments. + Such path arguments are implicitly coerced to file sets containing all files under that path: + - A path to a file turns into a file set containing that single file. + - A path to a directory turns into a file set containing all files _recursively_ in that directory. + + If the path points to a non-existent location, an error is thrown. + + ::: {.note} + Just like in Git, file sets cannot represent empty directories. + Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files. + ::: + + :::{.note} + File set coercion does _not_ add any of the files under the coerced paths to the store. + Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument. + This is in contrast to using [paths in string interpolation](https://nixos.org/manual/nix/stable/language/values.html#type-path), which does add the entire referenced path to the store. + ::: + + ### Example {#sec-fileset-path-coercion-example} + + Assume we are in a local directory with a file hierarchy like this: + ``` + ├─ a/ + │ ├─ x (file) + │ └─ b/ + │ └─ y (file) + └─ c/ + └─ d/ + ``` + + Here's a listing of which files get included when different path expressions get coerced to file sets: + - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted). + - `./a` as a file set contains both `a/x` and `a/b/y`. + - `./a/x` as a file set contains only `a/x`. + - `./a/b` as a file set contains only `a/b/y`. + - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files. +*/ +{ lib }: +let + + inherit (import ./internal.nix { inherit lib; }) + _coerce + _singleton + _coerceMany + _toSourceFilter + _fromSourceFilter + _unionMany + _fileFilter + _printFileset + _intersection + _difference + _fromFetchGit + _fetchGitSubmodulesMinver + _emptyWithoutBase + ; + + inherit (builtins) + isBool + isList + isPath + pathExists + seq + typeOf + nixVersion + ; + + inherit (lib.lists) + elemAt + imap0 + ; + + inherit (lib.path) + hasPrefix + splitRoot + ; + + inherit (lib.strings) + isStringLike + versionOlder + ; + + inherit (lib.filesystem) + pathType + ; + + inherit (lib.sources) + cleanSourceWith + ; + + inherit (lib.trivial) + isFunction + pipe + ; + +in { + + /* + Create a file set from a path that may or may not exist: + - If the path does exist, the path is [coerced to a file set](#sec-fileset-path-coercion). + - If the path does not exist, a file set containing no files is returned. + + Type: + maybeMissing :: Path -> FileSet + + Example: + # All files in the current directory, but excluding main.o if it exists + difference ./. (maybeMissing ./main.o) + */ + maybeMissing = + path: + if ! isPath path then + if isStringLike path then + throw '' + lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.'' + else + throw '' + lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead.'' + else if ! pathExists path then + _emptyWithoutBase + else + _singleton path; + + /* + Incrementally evaluate and trace a file set in a pretty way. + This function is only intended for debugging purposes. + The exact tracing format is unspecified and may change. + + This function takes a final argument to return. + In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns + the given file set argument. + + This variant is useful for tracing file sets in the Nix repl. + + Type: + trace :: FileSet -> Any -> Any + + Example: + trace (unions [ ./Makefile ./src ./tests/run.sh ]) null + => + trace: /home/user/src/myProject + trace: - Makefile (regular) + trace: - src (all files in directory) + trace: - tests + trace: - run.sh (regular) + null + */ + trace = + /* + The file set to trace. + + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + */ + fileset: + let + # "fileset" would be a better name, but that would clash with the argument name, + # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 + actualFileset = _coerce "lib.fileset.trace: Argument" fileset; + in + seq + (_printFileset actualFileset) + (x: x); + + /* + Incrementally evaluate and trace a file set in a pretty way. + This function is only intended for debugging purposes. + The exact tracing format is unspecified and may change. + + This function returns the given file set. + In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return. + + This variant is useful for tracing file sets passed as arguments to other functions. + + Type: + traceVal :: FileSet -> FileSet + + Example: + toSource { + root = ./.; + fileset = traceVal (unions [ + ./Makefile + ./src + ./tests/run.sh + ]); + } + => + trace: /home/user/src/myProject + trace: - Makefile (regular) + trace: - src (all files in directory) + trace: - tests + trace: - run.sh (regular) + "/nix/store/...-source" + */ + traceVal = + /* + The file set to trace and return. + + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + */ + fileset: + let + # "fileset" would be a better name, but that would clash with the argument name, + # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 + actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset; + in + seq + (_printFileset actualFileset) + # We could also return the original fileset argument here, + # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again + actualFileset; + + /* + Add the local files contained in `fileset` to the store as a single [store path](https://nixos.org/manual/nix/stable/glossary#gloss-store-path) rooted at `root`. + + The result is the store path as a string-like value, making it usable e.g. as the `src` of a derivation, or in string interpolation: + ```nix + stdenv.mkDerivation { + src = lib.fileset.toSource { ... }; + # ... + } + ``` + + The name of the store path is always `source`. + + Type: + toSource :: { + root :: Path, + fileset :: FileSet, + } -> SourceLike + + Example: + # Import the current directory into the store + # but only include files under ./src + toSource { + root = ./.; + fileset = ./src; + } + => "/nix/store/...-source" + + # Import the current directory into the store + # but only include ./Makefile and all files under ./src + toSource { + root = ./.; + fileset = union + ./Makefile + ./src; + } + => "/nix/store/...-source" + + # Trying to include a file outside the root will fail + toSource { + root = ./.; + fileset = unions [ + ./Makefile + ./src + ../LICENSE + ]; + } + => <error> + + # The root needs to point to a directory that contains all the files + toSource { + root = ../.; + fileset = unions [ + ./Makefile + ./src + ../LICENSE + ]; + } + => "/nix/store/...-source" + + # The root has to be a local filesystem path + toSource { + root = "/nix/store/...-source"; + fileset = ./.; + } + => <error> + */ + toSource = { + /* + (required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path. + Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`. + `root` has to be a directory. + + :::{.note} + Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store. + The only way to change which files get added to the store is by changing the `fileset` attribute. + ::: + */ + root, + /* + (required) The file set whose files to import into the store. + File sets can be created using other functions in this library. + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + + :::{.note} + If a directory does not recursively contain any file, it is omitted from the store path contents. + ::: + + */ + fileset, + }: + let + # We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement + filesetArg = fileset; + in + let + fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg; + rootFilesystemRoot = (splitRoot root).root; + filesetFilesystemRoot = (splitRoot fileset._internalBase).root; + sourceFilter = _toSourceFilter fileset; + in + if ! isPath root then + if root ? _isLibCleanSourceWith then + throw '' + lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead. + To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`. + Note that this only works for sources created from paths.'' + else if isStringLike root then + throw '' + lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead. + Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.'' + else + throw '' + lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.'' + # Currently all Nix paths have the same filesystem root, but this could change in the future. + # See also ../path/README.md + else if ! fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then + throw '' + lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${toString root}): + `root`: Filesystem root is "${toString rootFilesystemRoot}" + `fileset`: Filesystem root is "${toString filesetFilesystemRoot}" + Different filesystem roots are not supported.'' + else if ! pathExists root then + throw '' + lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.'' + else if pathType root != "directory" then + throw '' + lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions: + - If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function. + - If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as ${toString (dirOf root)}, and set `fileset` to the file path.'' + else if ! fileset._internalIsEmptyWithoutBase && ! hasPrefix root fileset._internalBase then + throw '' + lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions: + - 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 + seq sourceFilter + cleanSourceWith { + name = "source"; + src = root; + filter = sourceFilter; + }; + + /* + 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), + but takes just two file sets instead of a list. + See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). + + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. + + Type: + union :: FileSet -> FileSet -> FileSet + + Example: + # Create a file set containing the file `Makefile` + # and all files recursively in the `src` directory + union ./Makefile ./src + + # Create a file set containing the file `Makefile` + # and the LICENSE file from the parent directory + union ./Makefile ../LICENSE + */ + union = + # The first file set. + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + fileset1: + # The second file set. + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + fileset2: + _unionMany + (_coerceMany "lib.fileset.union" [ + { + context = "First argument"; + value = fileset1; + } + { + context = "Second argument"; + value = fileset2; + } + ]); + + /* + The file set containing all files that are in any of the given file sets. + This is the same as [`union`](#function-library-lib.fileset.unions), + but takes a list of file sets instead of just two. + See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). + + The given file sets are evaluated as lazily as possible, + with earlier elements being evaluated first if needed. + + Type: + unions :: [ FileSet ] -> FileSet + + Example: + # Create a file set containing selected files + unions [ + # Include the single file `Makefile` in the current directory + # This errors if the file doesn't exist + ./Makefile + + # Recursively include all files in the `src/code` directory + # If this directory is empty this has no effect + ./src/code + + # Include the files `run.sh` and `unit.c` from the `tests` directory + ./tests/run.sh + ./tests/unit.c + + # Include the `LICENSE` file from the parent directory + ../LICENSE + ] + */ + unions = + # A list of file sets. + # The elements can also be paths, + # which get [implicitly coerced to file sets](#sec-fileset-path-coercion). + filesets: + if ! isList filesets then + throw '' + lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.'' + else + pipe filesets [ + # Annotate the elements with context, used by _coerceMany for better errors + (imap0 (i: el: { + context = "Element ${toString i}"; + value = el; + })) + (_coerceMany "lib.fileset.unions") + _unionMany + ]; + + /* + The file set containing all files that are in both of two given file sets. + See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)). + + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. + + Type: + intersection :: FileSet -> FileSet -> FileSet + + Example: + # Limit the selected files to the ones in ./., so only ./src and ./Makefile + intersection ./. (unions [ ../LICENSE ./src ./Makefile ]) + */ + intersection = + # The first file set. + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + fileset1: + # The second file set. + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + fileset2: + let + filesets = _coerceMany "lib.fileset.intersection" [ + { + context = "First argument"; + value = fileset1; + } + { + context = "Second argument"; + value = fileset2; + } + ]; + in + _intersection + (elemAt filesets 0) + (elemAt filesets 1); + + /* + The file set containing all files from the first file set that are not in the second file set. + See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement). + + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. + + Type: + union :: FileSet -> FileSet -> FileSet + + Example: + # Create a file set containing all files from the current directory, + # except ones under ./tests + difference ./. ./tests + + let + # A set of Nix-related files + nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ]; + in + # Create a file set containing all files under ./tests, except ones in `nixFiles`, + # meaning only without ./tests/default.nix + difference ./tests nixFiles + */ + difference = + # The positive file set. + # The result can only contain files that are also in this file set. + # + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + positive: + # The negative file set. + # The result will never contain files that are also in this file set. + # + # This argument can also be a path, + # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + negative: + let + filesets = _coerceMany "lib.fileset.difference" [ + { + context = "First argument (positive set)"; + value = positive; + } + { + context = "Second argument (negative set)"; + value = negative; + } + ]; + in + _difference + (elemAt filesets 0) + (elemAt filesets 1); + + /* + Filter a file set to only contain files matching some predicate. + + Type: + fileFilter :: + ({ + name :: String, + type :: String, + hasExt :: String -> Bool, + ... + } -> Bool) + -> Path + -> FileSet + + Example: + # Include all regular `default.nix` files in the current directory + fileFilter (file: file.name == "default.nix") ./. + + # Include all non-Nix files from the current directory + fileFilter (file: ! file.hasExt "nix") ./. + + # Include all files that start with a "." in the current directory + fileFilter (file: hasPrefix "." file.name) ./. + + # Include all regular files (not symlinks or others) in the current directory + fileFilter (file: file.type == "regular") ./. + */ + fileFilter = + /* + The predicate function to call on all files contained in given file set. + A file is included in the resulting file set if this function returns true for it. + + This function is called with an attribute set containing these attributes: + + - `name` (String): The name of the file + + - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file. + This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path. + + - `hasExt` (String -> Bool): Whether the file has a certain file extension. + `hasExt ext` is true only if `hasSuffix ".${ext}" name`. + + This also means that e.g. for a file with name `.gitignore`, + `hasExt "gitignore"` is true. + + Other attributes may be added in the future. + */ + predicate: + # The path whose files to filter + path: + if ! isFunction predicate then + throw '' + lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.'' + else if ! isPath path then + if path._type or "" == "fileset" then + throw '' + lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead. + If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.'' + else + throw '' + lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.'' + else if ! pathExists path then + throw '' + lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.'' + else + _fileFilter predicate path; + + /* + Create a file set with the same files as a `lib.sources`-based value. + This does not import any of the files into the store. + + This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`. + + A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource). + + :::{.note} + File sets cannot represent empty directories. + Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories. + ::: + + Type: + fromSource :: SourceLike -> FileSet + + Example: + # There's no cleanSource-like function for file sets yet, + # but we can just convert cleanSource to a file set and use it that way + toSource { + root = ./.; + fileset = fromSource (lib.sources.cleanSource ./.); + } + + # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`), + # but removing a subdirectory using file set functions + difference + (fromSource (lib.sources.sourceByRegex ./. [ + "^README\.md$" + # This regex includes everything in ./doc + "^doc(/.*)?$" + ]) + ./doc/generated + + # Use cleanSource, but limit it to only include ./Makefile and files under ./src + intersection + (fromSource (lib.sources.cleanSource ./.)) + (unions [ + ./Makefile + ./src + ]); + */ + fromSource = source: + let + # This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`, + # which are technically internal to lib.sources, + # but we'll allow this since both libraries are in the same code base + # and this function is a bridge between them. + isFiltered = source ? _isLibCleanSourceWith; + path = if isFiltered then source.origSrc else source; + in + # We can only support sources created from paths + if ! isPath path then + if isStringLike path then + throw '' + lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead. + Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.'' + else + throw '' + lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.'' + else if ! pathExists path then + throw '' + lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.'' + else if isFiltered then + _fromSourceFilter path source.filter + else + # If there's no filter, no need to run the expensive conversion, all subpaths will be included + _singleton path; + + /* + Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. + + This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults. + + Type: + gitTracked :: Path -> FileSet + + Example: + # Include all files tracked by the Git repository in the current directory + gitTracked ./. + + # Include only files tracked by the Git repository in the parent directory + # that are also in the current directory + intersection ./. (gitTracked ../.) + */ + gitTracked = + /* + The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. + This directory must contain a `.git` file or subdirectory. + */ + path: + _fromFetchGit + "gitTracked" + "argument" + path + {}; + + /* + Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. + The first argument allows configuration with an attribute set, + while the second argument is the path to the Git working tree. + + `gitTrackedWith` does not perform any filtering when the path is a [Nix store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) and not a repository. + In this way, it accommodates the use case where the expression that makes the `gitTracked` call does not reside in an actual git repository anymore, + and has presumably already been fetched in a way that excludes untracked files. + Fetchers with such equivalent behavior include `builtins.fetchGit`, `builtins.fetchTree` (experimental), and `pkgs.fetchgit` when used without `leaveDotGit`. + + If you don't need the configuration, + you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead. + + This is equivalent to the result of [`unions`](#function-library-lib.fileset.unions) on all files returned by [`git ls-files`](https://git-scm.com/docs/git-ls-files) + (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default). + + :::{.warning} + Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit) + As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store, + without being re-usable by [`toSource`](#function-library-lib.fileset.toSource). + + This may change in the future. + ::: + + Type: + gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet + + Example: + # Include all files tracked by the Git repository in the current directory + # and any submodules under it + gitTracked { recurseSubmodules = true; } ./. + */ + gitTrackedWith = + { + /* + (optional, default: `false`) Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files. + + If `true`, this is equivalent to passing the [--recurse-submodules](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---recurse-submodules) flag to `git ls-files`. + */ + recurseSubmodules ? false, + }: + /* + The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. + This directory must contain a `.git` file or subdirectory. + */ + path: + if ! isBool recurseSubmodules then + throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead." + else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then + throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used." + else + _fromFetchGit + "gitTrackedWith" + "second argument" + path + # This is the only `fetchGit` parameter that makes sense in this context. + # We can't just pass `submodules = recurseSubmodules` here because + # this would fail for Nix versions that don't support `submodules`. + (lib.optionalAttrs recurseSubmodules { + submodules = true; + }); +} |