edencommon: skip flaky test everywhere (#372055)
[NixPkgs.git] / lib / fileset / default.nix
blobacb7ac10c2371681f70e0e1cfd001b222ecb0035
1 /**
2   <!-- This anchor is here for backwards compatibility -->
3   []{#sec-fileset}
5   The [`lib.fileset`](#sec-functions-library-fileset) library allows you to work with _file sets_.
6   A file set is a (mathematical) set of local files that can be added to the Nix store for use in Nix derivations.
7   File sets are easy and safe to use, providing obvious and composable semantics with good error messages to prevent mistakes.
9   # Overview {#sec-fileset-overview}
11   Basics:
12   - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion)
14   - [`lib.fileset.maybeMissing`](#function-library-lib.fileset.maybeMissing):
16     Create a file set from a path that may be missing.
18   - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace):
20     Pretty-print file sets for debugging.
22   - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource):
24     Add files in file sets to the store to use as derivation sources.
26   - [`lib.fileset.toList`](#function-library-lib.fileset.toList):
28     The list of files contained in a file set.
30   Combinators:
31   - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions):
33     Create a larger file set from all the files in multiple file sets.
35   - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection):
37     Create a smaller file set from only the files in both file sets.
39   - [`lib.fileset.difference`](#function-library-lib.fileset.difference):
41     Create a smaller file set containing all files that are in one file set, but not another one.
43   Filtering:
44   - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter):
46     Create a file set from all files that satisisfy a predicate in a directory.
48   Utilities:
49   - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource):
51     Create a file set from a `lib.sources`-based value.
53   - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith):
55     Create a file set from all tracked files in a local Git repository.
57   If you need more file set functions,
58   see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it.
60   # Implicit coercion from paths to file sets {#sec-fileset-path-coercion}
62   All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments.
63   Such path arguments are implicitly coerced to file sets containing all files under that path:
64   - A path to a file turns into a file set containing that single file.
65   - A path to a directory turns into a file set containing all files _recursively_ in that directory.
67   If the path points to a non-existent location, an error is thrown.
69   ::: {.note}
70   Just like in Git, file sets cannot represent empty directories.
71   Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files.
72   :::
74   :::{.note}
75   File set coercion does _not_ add any of the files under the coerced paths to the store.
76   Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument.
77   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.
78   :::
80   ## Example {#sec-fileset-path-coercion-example}
82   Assume we are in a local directory with a file hierarchy like this:
83   ```
84   ├─ a/
85   │  ├─ x (file)
86   │  └─ b/
87   │     └─ y (file)
88   └─ c/
89      └─ d/
90   ```
92   Here's a listing of which files get included when different path expressions get coerced to file sets:
93   - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted).
94   - `./a` as a file set contains both `a/x` and `a/b/y`.
95   - `./a/x` as a file set contains only `a/x`.
96   - `./a/b` as a file set contains only `a/b/y`.
97   - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files.
99 { lib }:
102   inherit (import ./internal.nix { inherit lib; })
103     _coerce
104     _singleton
105     _coerceMany
106     _toSourceFilter
107     _fromSourceFilter
108     _toList
109     _unionMany
110     _fileFilter
111     _printFileset
112     _intersection
113     _difference
114     _fromFetchGit
115     _fetchGitSubmodulesMinver
116     _emptyWithoutBase
117     ;
119   inherit (builtins)
120     isBool
121     isList
122     isPath
123     pathExists
124     seq
125     typeOf
126     nixVersion
127     ;
129   inherit (lib.lists)
130     elemAt
131     imap0
132     ;
134   inherit (lib.path)
135     hasPrefix
136     splitRoot
137     ;
139   inherit (lib.strings)
140     isStringLike
141     versionOlder
142     ;
144   inherit (lib.filesystem)
145     pathType
146     ;
148   inherit (lib.sources)
149     cleanSourceWith
150     ;
152   inherit (lib.trivial)
153     isFunction
154     pipe
155     ;
160   /**
161     Create a file set from a path that may or may not exist:
162     - If the path does exist, the path is [coerced to a file set](#sec-fileset-path-coercion).
163     - If the path does not exist, a file set containing no files is returned.
165     # Inputs
167     `path`
169     : 1\. Function argument
171     # Type
173     ```
174     maybeMissing :: Path -> FileSet
175     ```
177     # Examples
178     :::{.example}
179     ## `lib.fileset.maybeMissing` usage example
181     ```nix
182     # All files in the current directory, but excluding main.o if it exists
183     difference ./. (maybeMissing ./main.o)
184     ```
186     :::
187   */
188   maybeMissing =
189     path:
190     if !isPath path then
191       if isStringLike path then
192         throw ''lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.''
193       else
194         throw ''lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead.''
195     else if !pathExists path then
196       _emptyWithoutBase
197     else
198       _singleton path;
200   /**
201     Incrementally evaluate and trace a file set in a pretty way.
202     This function is only intended for debugging purposes.
203     The exact tracing format is unspecified and may change.
205     This function takes a final argument to return.
206     In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns
207     the given file set argument.
209     This variant is useful for tracing file sets in the Nix repl.
211     # Inputs
213     `fileset`
215     : The file set to trace.
217       This argument can also be a path,
218       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
220     `val`
222     : The value to return.
224     # Type
226     ```
227     trace :: FileSet -> Any -> Any
228     ```
230     # Examples
231     :::{.example}
232     ## `lib.fileset.trace` usage example
234     ```nix
235     trace (unions [ ./Makefile ./src ./tests/run.sh ]) null
236     =>
237     trace: /home/user/src/myProject
238     trace: - Makefile (regular)
239     trace: - src (all files in directory)
240     trace: - tests
241     trace:   - run.sh (regular)
242     null
243     ```
245     :::
246   */
247   trace =
248     fileset:
249     let
250       # "fileset" would be a better name, but that would clash with the argument name,
251       # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
252       actualFileset = _coerce "lib.fileset.trace: Argument" fileset;
253     in
254     seq (_printFileset actualFileset) (x: x);
256   /**
257     Incrementally evaluate and trace a file set in a pretty way.
258     This function is only intended for debugging purposes.
259     The exact tracing format is unspecified and may change.
261     This function returns the given file set.
262     In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return.
264     This variant is useful for tracing file sets passed as arguments to other functions.
266     # Inputs
268     `fileset`
270     : The file set to trace and return.
272       This argument can also be a path,
273       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
275     # Type
277     ```
278     traceVal :: FileSet -> FileSet
279     ```
281     # Examples
282     :::{.example}
283     ## `lib.fileset.traceVal` usage example
285     ```nix
286     toSource {
287       root = ./.;
288       fileset = traceVal (unions [
289         ./Makefile
290         ./src
291         ./tests/run.sh
292       ]);
293     }
294     =>
295     trace: /home/user/src/myProject
296     trace: - Makefile (regular)
297     trace: - src (all files in directory)
298     trace: - tests
299     trace:   - run.sh (regular)
300     "/nix/store/...-source"
301     ```
303     :::
304   */
305   traceVal =
306     fileset:
307     let
308       # "fileset" would be a better name, but that would clash with the argument name,
309       # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
310       actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset;
311     in
312     seq (_printFileset actualFileset)
313       # We could also return the original fileset argument here,
314       # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
315       actualFileset;
317   /**
318     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`.
320     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:
321     ```nix
322     stdenv.mkDerivation {
323       src = lib.fileset.toSource { ... };
324       # ...
325     }
326     ```
328     The name of the store path is always `source`.
330     # Inputs
332     Takes an attribute set with the following attributes
334     `root` (Path; _required_)
336     : 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.
337       Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`.
338       `root` has to be a directory.
340       :::{.note}
341       Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store.
342       The only way to change which files get added to the store is by changing the `fileset` attribute.
343       :::
345     `fileset` (FileSet; _required_)
347     : The file set whose files to import into the store.
348       File sets can be created using other functions in this library.
349       This argument can also be a path,
350       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
352       :::{.note}
353       If a directory does not recursively contain any file, it is omitted from the store path contents.
354       :::
356     # Type
358     ```
359     toSource :: {
360       root :: Path,
361       fileset :: FileSet,
362     } -> SourceLike
363     ```
365     # Examples
366     :::{.example}
367     ## `lib.fileset.toSource` usage example
369     ```nix
370     # Import the current directory into the store
371     # but only include files under ./src
372     toSource {
373       root = ./.;
374       fileset = ./src;
375     }
376     => "/nix/store/...-source"
378     # Import the current directory into the store
379     # but only include ./Makefile and all files under ./src
380     toSource {
381       root = ./.;
382       fileset = union
383         ./Makefile
384         ./src;
385     }
386     => "/nix/store/...-source"
388     # Trying to include a file outside the root will fail
389     toSource {
390       root = ./.;
391       fileset = unions [
392         ./Makefile
393         ./src
394         ../LICENSE
395       ];
396     }
397     => <error>
399     # The root needs to point to a directory that contains all the files
400     toSource {
401       root = ../.;
402       fileset = unions [
403         ./Makefile
404         ./src
405         ../LICENSE
406       ];
407     }
408     => "/nix/store/...-source"
410     # The root has to be a local filesystem path
411     toSource {
412       root = "/nix/store/...-source";
413       fileset = ./.;
414     }
415     => <error>
416     ```
418     :::
419   */
420   toSource =
421     {
422       root,
423       fileset,
424     }:
425     let
426       # We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement
427       filesetArg = fileset;
428     in
429     let
430       fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg;
431       rootFilesystemRoot = (splitRoot root).root;
432       filesetFilesystemRoot = (splitRoot fileset._internalBase).root;
433       sourceFilter = _toSourceFilter fileset;
434     in
435     if !isPath root then
436       if root ? _isLibCleanSourceWith then
437         throw ''
438           lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead.
439               To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`.
440               Note that this only works for sources created from paths.''
441       else if isStringLike root then
442         throw ''
443           lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead.
444               Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
445       else
446         throw ''lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.''
447     # Currently all Nix paths have the same filesystem root, but this could change in the future.
448     # See also ../path/README.md
449     else if !fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then
450       throw ''
451         lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${toString root}):
452             `root`: Filesystem root is "${toString rootFilesystemRoot}"
453             `fileset`: Filesystem root is "${toString filesetFilesystemRoot}"
454             Different filesystem roots are not supported.''
455     else if !pathExists root then
456       throw ''lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.''
457     else if pathType root != "directory" then
458       throw ''
459         lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions:
460             - If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function.
461             - 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.''
462     else if !fileset._internalIsEmptyWithoutBase && !hasPrefix root fileset._internalBase then
463       throw ''
464         lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions:
465             - Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path.
466             - Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.''
467     else
468       seq sourceFilter cleanSourceWith {
469         name = "source";
470         src = root;
471         filter = sourceFilter;
472       };
474   /**
475     The list of file paths contained in the given file set.
477     :::{.note}
478     This function is strict in the entire file set.
479     This is in contrast with combinators [`lib.fileset.union`](#function-library-lib.fileset.union),
480     [`lib.fileset.intersection`](#function-library-lib.fileset.intersection) and [`lib.fileset.difference`](#function-library-lib.fileset.difference).
482     Thus it is recommended to call `toList` on file sets created using the combinators,
483     instead of doing list processing on the result of `toList`.
484     :::
486     The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions).
488     # Inputs
490     `fileset`
492     : 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).
494     # Type
496     ```
497     toList :: FileSet -> [ Path ]
498     ```
500     # Examples
501     :::{.example}
502     ## `lib.fileset.toList` usage example
504     ```nix
505     toList ./.
506     [ ./README.md ./Makefile ./src/main.c ./src/main.h ]
508     toList (difference ./. ./src)
509     [ ./README.md ./Makefile ]
510     ```
512     :::
513   */
514   toList = fileset: _toList (_coerce "lib.fileset.toList: Argument" fileset);
516   /**
517     The file set containing all files that are in either of two given file sets.
518     This is the same as [`unions`](#function-library-lib.fileset.unions),
519     but takes just two file sets instead of a list.
520     See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
522     The given file sets are evaluated as lazily as possible,
523     with the first argument being evaluated first if needed.
525     # Inputs
527     `fileset1`
529     : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
531     `fileset2`
533     : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
535     # Type
537     ```
538     union :: FileSet -> FileSet -> FileSet
539     ```
541     # Examples
542     :::{.example}
543     ## `lib.fileset.union` usage example
545     ```nix
546     # Create a file set containing the file `Makefile`
547     # and all files recursively in the `src` directory
548     union ./Makefile ./src
550     # Create a file set containing the file `Makefile`
551     # and the LICENSE file from the parent directory
552     union ./Makefile ../LICENSE
553     ```
555     :::
556   */
557   union =
558     fileset1: fileset2:
559     _unionMany (
560       _coerceMany "lib.fileset.union" [
561         {
562           context = "First argument";
563           value = fileset1;
564         }
565         {
566           context = "Second argument";
567           value = fileset2;
568         }
569       ]
570     );
572   /**
573     The file set containing all files that are in any of the given file sets.
574     This is the same as [`union`](#function-library-lib.fileset.unions),
575     but takes a list of file sets instead of just two.
576     See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
578     The given file sets are evaluated as lazily as possible,
579     with earlier elements being evaluated first if needed.
581     # Inputs
583     `filesets`
585     : A list of file sets. The elements can also be paths, which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
587     # Type
589     ```
590     unions :: [ FileSet ] -> FileSet
591     ```
593     # Examples
594     :::{.example}
595     ## `lib.fileset.unions` usage example
597     ```nix
598     # Create a file set containing selected files
599     unions [
600       # Include the single file `Makefile` in the current directory
601       # This errors if the file doesn't exist
602       ./Makefile
604       # Recursively include all files in the `src/code` directory
605       # If this directory is empty this has no effect
606       ./src/code
608       # Include the files `run.sh` and `unit.c` from the `tests` directory
609       ./tests/run.sh
610       ./tests/unit.c
612       # Include the `LICENSE` file from the parent directory
613       ../LICENSE
614     ]
615     ```
617     :::
618   */
619   unions =
620     filesets:
621     if !isList filesets then
622       throw ''lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.''
623     else
624       pipe filesets [
625         # Annotate the elements with context, used by _coerceMany for better errors
626         (imap0 (
627           i: el: {
628             context = "Element ${toString i}";
629             value = el;
630           }
631         ))
632         (_coerceMany "lib.fileset.unions")
633         _unionMany
634       ];
636   /**
637     The file set containing all files that are in both of two given file sets.
638     See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)).
640     The given file sets are evaluated as lazily as possible,
641     with the first argument being evaluated first if needed.
643     # Inputs
645     `fileset1`
647     : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
649     `fileset2`
651     : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
653     # Type
655     ```
656     intersection :: FileSet -> FileSet -> FileSet
657     ```
659     # Examples
660     :::{.example}
661     ## `lib.fileset.intersection` usage example
663     ```nix
664     # Limit the selected files to the ones in ./., so only ./src and ./Makefile
665     intersection ./. (unions [ ../LICENSE ./src ./Makefile ])
666     ```
668     :::
669   */
670   intersection =
671     fileset1: fileset2:
672     let
673       filesets = _coerceMany "lib.fileset.intersection" [
674         {
675           context = "First argument";
676           value = fileset1;
677         }
678         {
679           context = "Second argument";
680           value = fileset2;
681         }
682       ];
683     in
684     _intersection (elemAt filesets 0) (elemAt filesets 1);
686   /**
687     The file set containing all files from the first file set that are not in the second file set.
688     See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement).
690     The given file sets are evaluated as lazily as possible,
691     with the first argument being evaluated first if needed.
693     # Inputs
695     `positive`
697     : 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).
699     `negative`
701     : 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).
703     # Type
705     ```
706     union :: FileSet -> FileSet -> FileSet
707     ```
709     # Examples
710     :::{.example}
711     ## `lib.fileset.difference` usage example
713     ```nix
714     # Create a file set containing all files from the current directory,
715     # except ones under ./tests
716     difference ./. ./tests
718     let
719       # A set of Nix-related files
720       nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ];
721     in
722     # Create a file set containing all files under ./tests, except ones in `nixFiles`,
723     # meaning only without ./tests/default.nix
724     difference ./tests nixFiles
725     ```
727     :::
728   */
729   difference =
730     positive: negative:
731     let
732       filesets = _coerceMany "lib.fileset.difference" [
733         {
734           context = "First argument (positive set)";
735           value = positive;
736         }
737         {
738           context = "Second argument (negative set)";
739           value = negative;
740         }
741       ];
742     in
743     _difference (elemAt filesets 0) (elemAt filesets 1);
745   /**
746     Filter a file set to only contain files matching some predicate.
748     # Inputs
750     `predicate`
752     : The predicate function to call on all files contained in given file set.
753       A file is included in the resulting file set if this function returns true for it.
755       This function is called with an attribute set containing these attributes:
757       - `name` (String): The name of the file
759       - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file.
760         This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path.
762       - `hasExt` (String -> Bool): Whether the file has a certain file extension.
763         `hasExt ext` is true only if `hasSuffix ".${ext}" name`.
765         This also means that e.g. for a file with name `.gitignore`,
766         `hasExt "gitignore"` is true.
768       Other attributes may be added in the future.
770     `path`
772     : The path whose files to filter
774     # Type
776     ```
777     fileFilter ::
778       ({
779         name :: String,
780         type :: String,
781         hasExt :: String -> Bool,
782         ...
783       } -> Bool)
784       -> Path
785       -> FileSet
786     ```
788     # Examples
789     :::{.example}
790     ## `lib.fileset.fileFilter` usage example
792     ```nix
793     # Include all regular `default.nix` files in the current directory
794     fileFilter (file: file.name == "default.nix") ./.
796     # Include all non-Nix files from the current directory
797     fileFilter (file: ! file.hasExt "nix") ./.
799     # Include all files that start with a "." in the current directory
800     fileFilter (file: hasPrefix "." file.name) ./.
802     # Include all regular files (not symlinks or others) in the current directory
803     fileFilter (file: file.type == "regular") ./.
804     ```
806     :::
807   */
808   fileFilter =
809     predicate: path:
810     if !isFunction predicate then
811       throw ''lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
812     else if !isPath path then
813       if path._type or "" == "fileset" then
814         throw ''
815           lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead.
816               If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.''
817       else
818         throw ''lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.''
819     else if !pathExists path then
820       throw ''lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.''
821     else
822       _fileFilter predicate path;
824   /**
825     Create a file set with the same files as a `lib.sources`-based value.
826     This does not import any of the files into the store.
828     This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`.
830     A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource).
832     :::{.note}
833     File sets cannot represent empty directories.
834     Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories.
835     :::
837     # Inputs
839     `source`
841     : 1\. Function argument
843     # Type
845     ```
846     fromSource :: SourceLike -> FileSet
847     ```
849     # Examples
850     :::{.example}
851     ## `lib.fileset.fromSource` usage example
853     ```nix
854     # There's no cleanSource-like function for file sets yet,
855     # but we can just convert cleanSource to a file set and use it that way
856     toSource {
857       root = ./.;
858       fileset = fromSource (lib.sources.cleanSource ./.);
859     }
861     # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`),
862     # but removing a subdirectory using file set functions
863     difference
864       (fromSource (lib.sources.sourceByRegex ./. [
865         "^README\.md$"
866         # This regex includes everything in ./doc
867         "^doc(/.*)?$"
868       ])
869       ./doc/generated
871     # Use cleanSource, but limit it to only include ./Makefile and files under ./src
872     intersection
873       (fromSource (lib.sources.cleanSource ./.))
874       (unions [
875         ./Makefile
876         ./src
877       ]);
878     ```
880     :::
881   */
882   fromSource =
883     source:
884     let
885       # This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`,
886       # which are technically internal to lib.sources,
887       # but we'll allow this since both libraries are in the same code base
888       # and this function is a bridge between them.
889       isFiltered = source ? _isLibCleanSourceWith;
890       path = if isFiltered then source.origSrc else source;
891     in
892     # We can only support sources created from paths
893     if !isPath path then
894       if isStringLike path then
895         throw ''
896           lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead.
897               Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.''
898       else
899         throw ''lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.''
900     else if !pathExists path then
901       throw ''lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.''
902     else if isFiltered then
903       _fromSourceFilter path source.filter
904     else
905       # If there's no filter, no need to run the expensive conversion, all subpaths will be included
906       _singleton path;
908   /**
909     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.
911     This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults.
913     # Inputs
915     `path`
917     : The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
918       This directory must contain a `.git` file or subdirectory.
920     # Type
922     ```
923     gitTracked :: Path -> FileSet
924     ```
926     # Examples
927     :::{.example}
928     ## `lib.fileset.gitTracked` usage example
930     ```nix
931     # Include all files tracked by the Git repository in the current directory
932     gitTracked ./.
934     # Include only files tracked by the Git repository in the parent directory
935     # that are also in the current directory
936     intersection ./. (gitTracked ../.)
937     ```
939     :::
940   */
941   gitTracked = path: _fromFetchGit "gitTracked" "argument" path { };
943   /**
944     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.
945     The first argument allows configuration with an attribute set,
946     while the second argument is the path to the Git working tree.
948     `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.
949     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,
950     and has presumably already been fetched in a way that excludes untracked files.
951     Fetchers with such equivalent behavior include `builtins.fetchGit`, `builtins.fetchTree` (experimental), and `pkgs.fetchgit` when used without `leaveDotGit`.
953     If you don't need the configuration,
954     you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead.
956     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)
957     (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default).
959     :::{.warning}
960     Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit)
961     As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store,
962     without being re-usable by [`toSource`](#function-library-lib.fileset.toSource).
964     This may change in the future.
965     :::
967     # Inputs
969     `options` (attribute set)
970     : `recurseSubmodules` (optional, default: `false`)
971       : Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files.
972         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`.
974       `path`
975       : The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
976         This directory must contain a `.git` file or subdirectory.
978     # Type
980     ```
981     gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet
982     ```
984     # Examples
985     :::{.example}
986     ## `lib.fileset.gitTrackedWith` usage example
988     ```nix
989     # Include all files tracked by the Git repository in the current directory
990     # and any submodules under it
991     gitTracked { recurseSubmodules = true; } ./.
992     ```
994     :::
995   */
996   gitTrackedWith =
997     {
998       recurseSubmodules ? false,
999     }:
1000     path:
1001     if !isBool recurseSubmodules then
1002       throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead."
1003     else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
1004       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."
1005     else
1006       _fromFetchGit "gitTrackedWith" "second argument" path
1007         # This is the only `fetchGit` parameter that makes sense in this context.
1008         # We can't just pass `submodules = recurseSubmodules` here because
1009         # this would fail for Nix versions that don't support `submodules`.
1010         (
1011           lib.optionalAttrs recurseSubmodules {
1012             submodules = true;
1013           }
1014         );