vuls: init at 0.27.0
[NixPkgs.git] / lib / fileset / default.nix
blobbb327226731ee86d664eae39d7c0ac2e59deace4
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.
61   # Implicit coercion from paths to file sets {#sec-fileset-path-coercion}
63   All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments.
64   Such path arguments are implicitly coerced to file sets containing all files under that path:
65   - A path to a file turns into a file set containing that single file.
66   - A path to a directory turns into a file set containing all files _recursively_ in that directory.
68   If the path points to a non-existent location, an error is thrown.
70   ::: {.note}
71   Just like in Git, file sets cannot represent empty directories.
72   Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files.
73   :::
75   :::{.note}
76   File set coercion does _not_ add any of the files under the coerced paths to the store.
77   Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument.
78   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.
79   :::
81   ## Example {#sec-fileset-path-coercion-example}
83   Assume we are in a local directory with a file hierarchy like this:
84   ```
85   ├─ a/
86   │  ├─ x (file)
87   │  └─ b/
88   │     └─ y (file)
89   └─ c/
90      └─ d/
91   ```
93   Here's a listing of which files get included when different path expressions get coerced to file sets:
94   - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted).
95   - `./a` as a file set contains both `a/x` and `a/b/y`.
96   - `./a/x` as a file set contains only `a/x`.
97   - `./a/b` as a file set contains only `a/b/y`.
98   - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files.
100 { lib }:
103   inherit (import ./internal.nix { inherit lib; })
104     _coerce
105     _singleton
106     _coerceMany
107     _toSourceFilter
108     _fromSourceFilter
109     _toList
110     _unionMany
111     _fileFilter
112     _printFileset
113     _intersection
114     _difference
115     _fromFetchGit
116     _fetchGitSubmodulesMinver
117     _emptyWithoutBase
118     ;
120   inherit (builtins)
121     isBool
122     isList
123     isPath
124     pathExists
125     seq
126     typeOf
127     nixVersion
128     ;
130   inherit (lib.lists)
131     elemAt
132     imap0
133     ;
135   inherit (lib.path)
136     hasPrefix
137     splitRoot
138     ;
140   inherit (lib.strings)
141     isStringLike
142     versionOlder
143     ;
145   inherit (lib.filesystem)
146     pathType
147     ;
149   inherit (lib.sources)
150     cleanSourceWith
151     ;
153   inherit (lib.trivial)
154     isFunction
155     pipe
156     ;
158 in {
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.
166     # Inputs
168     `path`
170     : 1\. Function argument
172     # Type
174     ```
175     maybeMissing :: Path -> FileSet
176     ```
178     # Examples
179     :::{.example}
180     ## `lib.fileset.maybeMissing` usage example
182     ```nix
183     # All files in the current directory, but excluding main.o if it exists
184     difference ./. (maybeMissing ./main.o)
185     ```
187     :::
188   */
189   maybeMissing =
190     path:
191     if ! isPath path then
192       if isStringLike path then
193         throw ''
194           lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.''
195       else
196         throw ''
197           lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead.''
198     else if ! pathExists path then
199       _emptyWithoutBase
200     else
201       _singleton path;
203   /**
204     Incrementally evaluate and trace a file set in a pretty way.
205     This function is only intended for debugging purposes.
206     The exact tracing format is unspecified and may change.
208     This function takes a final argument to return.
209     In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns
210     the given file set argument.
212     This variant is useful for tracing file sets in the Nix repl.
215     # Inputs
217     `fileset`
219     : The file set to trace.
221       This argument can also be a path,
222       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
224     `val`
226     : The value to return.
228     # Type
230     ```
231     trace :: FileSet -> Any -> Any
232     ```
234     # Examples
235     :::{.example}
236     ## `lib.fileset.trace` usage example
238     ```nix
239     trace (unions [ ./Makefile ./src ./tests/run.sh ]) null
240     =>
241     trace: /home/user/src/myProject
242     trace: - Makefile (regular)
243     trace: - src (all files in directory)
244     trace: - tests
245     trace:   - run.sh (regular)
246     null
247     ```
249     :::
250   */
251   trace = fileset:
252     let
253       # "fileset" would be a better name, but that would clash with the argument name,
254       # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
255       actualFileset = _coerce "lib.fileset.trace: Argument" fileset;
256     in
257     seq
258       (_printFileset actualFileset)
259       (x: x);
261   /**
262     Incrementally evaluate and trace a file set in a pretty way.
263     This function is only intended for debugging purposes.
264     The exact tracing format is unspecified and may change.
266     This function returns the given file set.
267     In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return.
269     This variant is useful for tracing file sets passed as arguments to other functions.
272     # Inputs
274     `fileset`
276     : The file set to trace and return.
278       This argument can also be a path,
279       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
281     # Type
283     ```
284     traceVal :: FileSet -> FileSet
285     ```
287     # Examples
288     :::{.example}
289     ## `lib.fileset.traceVal` usage example
291     ```nix
292     toSource {
293       root = ./.;
294       fileset = traceVal (unions [
295         ./Makefile
296         ./src
297         ./tests/run.sh
298       ]);
299     }
300     =>
301     trace: /home/user/src/myProject
302     trace: - Makefile (regular)
303     trace: - src (all files in directory)
304     trace: - tests
305     trace:   - run.sh (regular)
306     "/nix/store/...-source"
307     ```
309     :::
310   */
311   traceVal = fileset:
312     let
313       # "fileset" would be a better name, but that would clash with the argument name,
314       # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
315       actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset;
316     in
317     seq
318       (_printFileset actualFileset)
319       # We could also return the original fileset argument here,
320       # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
321       actualFileset;
323   /**
324     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`.
326     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:
327     ```nix
328     stdenv.mkDerivation {
329       src = lib.fileset.toSource { ... };
330       # ...
331     }
332     ```
334     The name of the store path is always `source`.
336     # Inputs
338     Takes an attribute set with the following attributes
340     `root` (Path; _required_)
342     : 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.
343       Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`.
344       `root` has to be a directory.
346       :::{.note}
347       Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store.
348       The only way to change which files get added to the store is by changing the `fileset` attribute.
349       :::
351     `fileset` (FileSet; _required_)
353     : The file set whose files to import into the store.
354       File sets can be created using other functions in this library.
355       This argument can also be a path,
356       which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
358       :::{.note}
359       If a directory does not recursively contain any file, it is omitted from the store path contents.
360       :::
362     # Type
364     ```
365     toSource :: {
366       root :: Path,
367       fileset :: FileSet,
368     } -> SourceLike
369     ```
371     # Examples
372     :::{.example}
373     ## `lib.fileset.toSource` usage example
375     ```nix
376     # Import the current directory into the store
377     # but only include files under ./src
378     toSource {
379       root = ./.;
380       fileset = ./src;
381     }
382     => "/nix/store/...-source"
384     # Import the current directory into the store
385     # but only include ./Makefile and all files under ./src
386     toSource {
387       root = ./.;
388       fileset = union
389         ./Makefile
390         ./src;
391     }
392     => "/nix/store/...-source"
394     # Trying to include a file outside the root will fail
395     toSource {
396       root = ./.;
397       fileset = unions [
398         ./Makefile
399         ./src
400         ../LICENSE
401       ];
402     }
403     => <error>
405     # The root needs to point to a directory that contains all the files
406     toSource {
407       root = ../.;
408       fileset = unions [
409         ./Makefile
410         ./src
411         ../LICENSE
412       ];
413     }
414     => "/nix/store/...-source"
416     # The root has to be a local filesystem path
417     toSource {
418       root = "/nix/store/...-source";
419       fileset = ./.;
420     }
421     => <error>
422     ```
424     :::
425   */
426   toSource = {
427     root,
428     fileset,
429   }:
430     let
431       # We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement
432       filesetArg = fileset;
433     in
434     let
435       fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg;
436       rootFilesystemRoot = (splitRoot root).root;
437       filesetFilesystemRoot = (splitRoot fileset._internalBase).root;
438       sourceFilter = _toSourceFilter fileset;
439     in
440     if ! isPath root then
441       if root ? _isLibCleanSourceWith then
442         throw ''
443           lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead.
444               To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`.
445               Note that this only works for sources created from paths.''
446       else if isStringLike root then
447         throw ''
448           lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead.
449               Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
450       else
451         throw ''
452           lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.''
453     # Currently all Nix paths have the same filesystem root, but this could change in the future.
454     # See also ../path/README.md
455     else if ! fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then
456       throw ''
457         lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${toString root}):
458             `root`: Filesystem root is "${toString rootFilesystemRoot}"
459             `fileset`: Filesystem root is "${toString filesetFilesystemRoot}"
460             Different filesystem roots are not supported.''
461     else if ! pathExists root then
462       throw ''
463         lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.''
464     else if pathType root != "directory" then
465       throw ''
466         lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions:
467             - If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function.
468             - 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.''
469     else if ! fileset._internalIsEmptyWithoutBase && ! hasPrefix root fileset._internalBase then
470       throw ''
471         lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions:
472             - Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path.
473             - Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.''
474     else
475       seq sourceFilter
476       cleanSourceWith {
477         name = "source";
478         src = root;
479         filter = sourceFilter;
480       };
483   /**
484     The list of file paths contained in the given file set.
486     :::{.note}
487     This function is strict in the entire file set.
488     This is in contrast with combinators [`lib.fileset.union`](#function-library-lib.fileset.union),
489     [`lib.fileset.intersection`](#function-library-lib.fileset.intersection) and [`lib.fileset.difference`](#function-library-lib.fileset.difference).
491     Thus it is recommended to call `toList` on file sets created using the combinators,
492     instead of doing list processing on the result of `toList`.
493     :::
495     The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions).
498     # Inputs
500     `fileset`
502     : 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).
504     # Type
506     ```
507     toList :: FileSet -> [ Path ]
508     ```
510     # Examples
511     :::{.example}
512     ## `lib.fileset.toList` usage example
514     ```nix
515     toList ./.
516     [ ./README.md ./Makefile ./src/main.c ./src/main.h ]
518     toList (difference ./. ./src)
519     [ ./README.md ./Makefile ]
520     ```
522     :::
523   */
524   toList = fileset:
525     _toList (_coerce "lib.fileset.toList: Argument" fileset);
527   /**
528     The file set containing all files that are in either of two given file sets.
529     This is the same as [`unions`](#function-library-lib.fileset.unions),
530     but takes just two file sets instead of a list.
531     See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
533     The given file sets are evaluated as lazily as possible,
534     with the first argument being evaluated first if needed.
537     # Inputs
539     `fileset1`
541     : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
543     `fileset2`
545     : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
547     # Type
549     ```
550     union :: FileSet -> FileSet -> FileSet
551     ```
553     # Examples
554     :::{.example}
555     ## `lib.fileset.union` usage example
557     ```nix
558     # Create a file set containing the file `Makefile`
559     # and all files recursively in the `src` directory
560     union ./Makefile ./src
562     # Create a file set containing the file `Makefile`
563     # and the LICENSE file from the parent directory
564     union ./Makefile ../LICENSE
565     ```
567     :::
568   */
569   union =
570     fileset1:
571     fileset2:
572     _unionMany
573       (_coerceMany "lib.fileset.union" [
574         {
575           context = "First argument";
576           value = fileset1;
577         }
578         {
579           context = "Second argument";
580           value = fileset2;
581         }
582       ]);
584   /**
585     The file set containing all files that are in any of the given file sets.
586     This is the same as [`union`](#function-library-lib.fileset.unions),
587     but takes a list of file sets instead of just two.
588     See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
590     The given file sets are evaluated as lazily as possible,
591     with earlier elements being evaluated first if needed.
594     # Inputs
596     `filesets`
598     : A list of file sets. The elements can also be paths, which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
600     # Type
602     ```
603     unions :: [ FileSet ] -> FileSet
604     ```
606     # Examples
607     :::{.example}
608     ## `lib.fileset.unions` usage example
610     ```nix
611     # Create a file set containing selected files
612     unions [
613       # Include the single file `Makefile` in the current directory
614       # This errors if the file doesn't exist
615       ./Makefile
617       # Recursively include all files in the `src/code` directory
618       # If this directory is empty this has no effect
619       ./src/code
621       # Include the files `run.sh` and `unit.c` from the `tests` directory
622       ./tests/run.sh
623       ./tests/unit.c
625       # Include the `LICENSE` file from the parent directory
626       ../LICENSE
627     ]
628     ```
630     :::
631   */
632   unions =
633     filesets:
634     if ! isList filesets then
635       throw ''
636         lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.''
637     else
638       pipe filesets [
639         # Annotate the elements with context, used by _coerceMany for better errors
640         (imap0 (i: el: {
641           context = "Element ${toString i}";
642           value = el;
643         }))
644         (_coerceMany "lib.fileset.unions")
645         _unionMany
646       ];
648   /**
649     The file set containing all files that are in both of two given file sets.
650     See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)).
652     The given file sets are evaluated as lazily as possible,
653     with the first argument being evaluated first if needed.
656     # Inputs
658     `fileset1`
660     : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
662     `fileset2`
664     : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
666     # Type
668     ```
669     intersection :: FileSet -> FileSet -> FileSet
670     ```
672     # Examples
673     :::{.example}
674     ## `lib.fileset.intersection` usage example
676     ```nix
677     # Limit the selected files to the ones in ./., so only ./src and ./Makefile
678     intersection ./. (unions [ ../LICENSE ./src ./Makefile ])
679     ```
681     :::
682   */
683   intersection =
684     fileset1:
685     fileset2:
686     let
687       filesets = _coerceMany "lib.fileset.intersection" [
688         {
689           context = "First argument";
690           value = fileset1;
691         }
692         {
693           context = "Second argument";
694           value = fileset2;
695         }
696       ];
697     in
698     _intersection
699       (elemAt filesets 0)
700       (elemAt filesets 1);
702   /**
703     The file set containing all files from the first file set that are not in the second file set.
704     See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement).
706     The given file sets are evaluated as lazily as possible,
707     with the first argument being evaluated first if needed.
710     # Inputs
712     `positive`
714     : 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).
716     `negative`
718     : 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).
720     # Type
722     ```
723     union :: FileSet -> FileSet -> FileSet
724     ```
726     # Examples
727     :::{.example}
728     ## `lib.fileset.difference` usage example
730     ```nix
731     # Create a file set containing all files from the current directory,
732     # except ones under ./tests
733     difference ./. ./tests
735     let
736       # A set of Nix-related files
737       nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ];
738     in
739     # Create a file set containing all files under ./tests, except ones in `nixFiles`,
740     # meaning only without ./tests/default.nix
741     difference ./tests nixFiles
742     ```
744     :::
745   */
746   difference =
747     positive:
748     negative:
749     let
750       filesets = _coerceMany "lib.fileset.difference" [
751         {
752           context = "First argument (positive set)";
753           value = positive;
754         }
755         {
756           context = "Second argument (negative set)";
757           value = negative;
758         }
759       ];
760     in
761     _difference
762       (elemAt filesets 0)
763       (elemAt filesets 1);
765   /**
766     Filter a file set to only contain files matching some predicate.
769     # Inputs
771     `predicate`
773     : The predicate function to call on all files contained in given file set.
774       A file is included in the resulting file set if this function returns true for it.
776       This function is called with an attribute set containing these attributes:
778       - `name` (String): The name of the file
780       - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file.
781         This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path.
783       - `hasExt` (String -> Bool): Whether the file has a certain file extension.
784         `hasExt ext` is true only if `hasSuffix ".${ext}" name`.
786         This also means that e.g. for a file with name `.gitignore`,
787         `hasExt "gitignore"` is true.
789       Other attributes may be added in the future.
791     `path`
793     : The path whose files to filter
795     # Type
797     ```
798     fileFilter ::
799       ({
800         name :: String,
801         type :: String,
802         hasExt :: String -> Bool,
803         ...
804       } -> Bool)
805       -> Path
806       -> FileSet
807     ```
809     # Examples
810     :::{.example}
811     ## `lib.fileset.fileFilter` usage example
813     ```nix
814     # Include all regular `default.nix` files in the current directory
815     fileFilter (file: file.name == "default.nix") ./.
817     # Include all non-Nix files from the current directory
818     fileFilter (file: ! file.hasExt "nix") ./.
820     # Include all files that start with a "." in the current directory
821     fileFilter (file: hasPrefix "." file.name) ./.
823     # Include all regular files (not symlinks or others) in the current directory
824     fileFilter (file: file.type == "regular") ./.
825     ```
827     :::
828   */
829   fileFilter =
830     predicate:
831     path:
832     if ! isFunction predicate then
833       throw ''
834         lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
835     else if ! isPath path then
836       if path._type or "" == "fileset" then
837         throw ''
838           lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead.
839               If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.''
840       else
841         throw ''
842           lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.''
843     else if ! pathExists path then
844       throw ''
845         lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.''
846     else
847       _fileFilter predicate path;
849   /**
850     Create a file set with the same files as a `lib.sources`-based value.
851     This does not import any of the files into the store.
853     This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`.
855     A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource).
857     :::{.note}
858     File sets cannot represent empty directories.
859     Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories.
860     :::
863     # Inputs
865     `source`
867     : 1\. Function argument
869     # Type
871     ```
872     fromSource :: SourceLike -> FileSet
873     ```
875     # Examples
876     :::{.example}
877     ## `lib.fileset.fromSource` usage example
879     ```nix
880     # There's no cleanSource-like function for file sets yet,
881     # but we can just convert cleanSource to a file set and use it that way
882     toSource {
883       root = ./.;
884       fileset = fromSource (lib.sources.cleanSource ./.);
885     }
887     # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`),
888     # but removing a subdirectory using file set functions
889     difference
890       (fromSource (lib.sources.sourceByRegex ./. [
891         "^README\.md$"
892         # This regex includes everything in ./doc
893         "^doc(/.*)?$"
894       ])
895       ./doc/generated
897     # Use cleanSource, but limit it to only include ./Makefile and files under ./src
898     intersection
899       (fromSource (lib.sources.cleanSource ./.))
900       (unions [
901         ./Makefile
902         ./src
903       ]);
904     ```
906     :::
907   */
908   fromSource = source:
909     let
910       # This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`,
911       # which are technically internal to lib.sources,
912       # but we'll allow this since both libraries are in the same code base
913       # and this function is a bridge between them.
914       isFiltered = source ? _isLibCleanSourceWith;
915       path = if isFiltered then source.origSrc else source;
916     in
917     # We can only support sources created from paths
918     if ! isPath path then
919       if isStringLike path then
920         throw ''
921           lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead.
922               Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.''
923       else
924         throw ''
925           lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.''
926     else if ! pathExists path then
927       throw ''
928         lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.''
929     else if isFiltered then
930       _fromSourceFilter path source.filter
931     else
932       # If there's no filter, no need to run the expensive conversion, all subpaths will be included
933       _singleton path;
935   /**
936     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.
938     This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults.
941     # Inputs
943     `path`
945     : The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
946       This directory must contain a `.git` file or subdirectory.
948     # Type
950     ```
951     gitTracked :: Path -> FileSet
952     ```
954     # Examples
955     :::{.example}
956     ## `lib.fileset.gitTracked` usage example
958     ```nix
959     # Include all files tracked by the Git repository in the current directory
960     gitTracked ./.
962     # Include only files tracked by the Git repository in the parent directory
963     # that are also in the current directory
964     intersection ./. (gitTracked ../.)
965     ```
967     :::
968   */
969   gitTracked =
970     path:
971     _fromFetchGit
972       "gitTracked"
973       "argument"
974       path
975       {};
977   /**
978     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.
979     The first argument allows configuration with an attribute set,
980     while the second argument is the path to the Git working tree.
982     `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.
983     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,
984     and has presumably already been fetched in a way that excludes untracked files.
985     Fetchers with such equivalent behavior include `builtins.fetchGit`, `builtins.fetchTree` (experimental), and `pkgs.fetchgit` when used without `leaveDotGit`.
987     If you don't need the configuration,
988     you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead.
990     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)
991     (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default).
993     :::{.warning}
994     Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit)
995     As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store,
996     without being re-usable by [`toSource`](#function-library-lib.fileset.toSource).
998     This may change in the future.
999     :::
1002     # Inputs
1004     `options` (attribute set)
1005     : `recurseSubmodules` (optional, default: `false`)
1006       : Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files.
1007         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`.
1009       `path`
1010       : The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
1011         This directory must contain a `.git` file or subdirectory.
1013     # Type
1015     ```
1016     gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet
1017     ```
1019     # Examples
1020     :::{.example}
1021     ## `lib.fileset.gitTrackedWith` usage example
1023     ```nix
1024     # Include all files tracked by the Git repository in the current directory
1025     # and any submodules under it
1026     gitTracked { recurseSubmodules = true; } ./.
1027     ```
1029     :::
1030   */
1031   gitTrackedWith =
1032     {
1033       recurseSubmodules ? false,
1034     }:
1035     path:
1036     if ! isBool recurseSubmodules then
1037       throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead."
1038     else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
1039       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."
1040     else
1041       _fromFetchGit
1042         "gitTrackedWith"
1043         "second argument"
1044         path
1045         # This is the only `fetchGit` parameter that makes sense in this context.
1046         # We can't just pass `submodules = recurseSubmodules` here because
1047         # this would fail for Nix versions that don't support `submodules`.
1048         (lib.optionalAttrs recurseSubmodules {
1049           submodules = true;
1050         });