1/**
2 <!-- This anchor is here for backwards compatibility -->
3 []{#sec-fileset}
4
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.
8
9 # Overview {#sec-fileset-overview}
10
11 Basics:
12 - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion)
13
14 - [`lib.fileset.maybeMissing`](#function-library-lib.fileset.maybeMissing):
15
16 Create a file set from a path that may be missing.
17
18 - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace):
19
20 Pretty-print file sets for debugging.
21
22 - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource):
23
24 Add files in file sets to the store to use as derivation sources.
25
26 - [`lib.fileset.toList`](#function-library-lib.fileset.toList):
27
28 The list of files contained in a file set.
29
30 Combinators:
31 - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions):
32
33 Create a larger file set from all the files in multiple file sets.
34
35 - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection):
36
37 Create a smaller file set from only the files in both file sets.
38
39 - [`lib.fileset.difference`](#function-library-lib.fileset.difference):
40
41 Create a smaller file set containing all files that are in one file set, but not another one.
42
43 Filtering:
44 - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter):
45
46 Create a file set from all files that satisisfy a predicate in a directory.
47
48 Utilities:
49 - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource):
50
51 Create a file set from a `lib.sources`-based value.
52
53 - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith):
54
55 Create a file set from all tracked files in a local Git repository.
56
57 If you need more file set functions,
58 see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it.
59
60
61 # Implicit coercion from paths to file sets {#sec-fileset-path-coercion}
62
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.
67
68 If the path points to a non-existent location, an error is thrown.
69
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 :::
74
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 :::
80
81 ## Example {#sec-fileset-path-coercion-example}
82
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 ```
92
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.
99*/
100{ lib }:
101let
102
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 ;
119
120 inherit (builtins)
121 isBool
122 isList
123 isPath
124 pathExists
125 seq
126 typeOf
127 nixVersion
128 ;
129
130 inherit (lib.lists)
131 elemAt
132 imap0
133 ;
134
135 inherit (lib.path)
136 hasPrefix
137 splitRoot
138 ;
139
140 inherit (lib.strings)
141 isStringLike
142 versionOlder
143 ;
144
145 inherit (lib.filesystem)
146 pathType
147 ;
148
149 inherit (lib.sources)
150 cleanSourceWith
151 ;
152
153 inherit (lib.trivial)
154 isFunction
155 pipe
156 ;
157
158in {
159
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.
164
165
166 # Inputs
167
168 `path`
169
170 : 1\. Function argument
171
172 # Type
173
174 ```
175 maybeMissing :: Path -> FileSet
176 ```
177
178 # Examples
179 :::{.example}
180 ## `lib.fileset.maybeMissing` usage example
181
182 ```nix
183 # All files in the current directory, but excluding main.o if it exists
184 difference ./. (maybeMissing ./main.o)
185 ```
186
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;
202
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.
207
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.
211
212 This variant is useful for tracing file sets in the Nix repl.
213
214
215 # Inputs
216
217 `fileset`
218
219 : The file set to trace.
220
221 This argument can also be a path,
222 which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
223
224 `val`
225
226 : The value to return.
227
228 # Type
229
230 ```
231 trace :: FileSet -> Any -> Any
232 ```
233
234 # Examples
235 :::{.example}
236 ## `lib.fileset.trace` usage example
237
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 ```
248
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);
260
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.
265
266 This function returns the given file set.
267 In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return.
268
269 This variant is useful for tracing file sets passed as arguments to other functions.
270
271
272 # Inputs
273
274 `fileset`
275
276 : The file set to trace and return.
277
278 This argument can also be a path,
279 which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
280
281 # Type
282
283 ```
284 traceVal :: FileSet -> FileSet
285 ```
286
287 # Examples
288 :::{.example}
289 ## `lib.fileset.traceVal` usage example
290
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 ```
308
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;
322
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`.
325
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 ```
333
334 The name of the store path is always `source`.
335
336 # Inputs
337
338 Takes an attribute set with the following attributes
339
340 `root` (Path; _required_)
341
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.
345
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 :::
350
351 `fileset` (FileSet; _required_)
352
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).
357
358 :::{.note}
359 If a directory does not recursively contain any file, it is omitted from the store path contents.
360 :::
361
362 # Type
363
364 ```
365 toSource :: {
366 root :: Path,
367 fileset :: FileSet,
368 } -> SourceLike
369 ```
370
371 # Examples
372 :::{.example}
373 ## `lib.fileset.toSource` usage example
374
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"
383
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"
393
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>
404
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"
415
416 # The root has to be a local filesystem path
417 toSource {
418 root = "/nix/store/...-source";
419 fileset = ./.;
420 }
421 => <error>
422 ```
423
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 };
481
482
483 /**
484 The list of file paths contained in the given file set.
485
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).
490
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 :::
494
495 The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions).
496
497
498 # Inputs
499
500 `fileset`
501
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).
503
504 # Type
505
506 ```
507 toList :: FileSet -> [ Path ]
508 ```
509
510 # Examples
511 :::{.example}
512 ## `lib.fileset.toList` usage example
513
514 ```nix
515 toList ./.
516 [ ./README.md ./Makefile ./src/main.c ./src/main.h ]
517
518 toList (difference ./. ./src)
519 [ ./README.md ./Makefile ]
520 ```
521
522 :::
523 */
524 toList = fileset:
525 _toList (_coerce "lib.fileset.toList: Argument" fileset);
526
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)).
532
533 The given file sets are evaluated as lazily as possible,
534 with the first argument being evaluated first if needed.
535
536
537 # Inputs
538
539 `fileset1`
540
541 : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
542
543 `fileset2`
544
545 : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
546
547 # Type
548
549 ```
550 union :: FileSet -> FileSet -> FileSet
551 ```
552
553 # Examples
554 :::{.example}
555 ## `lib.fileset.union` usage example
556
557 ```nix
558 # Create a file set containing the file `Makefile`
559 # and all files recursively in the `src` directory
560 union ./Makefile ./src
561
562 # Create a file set containing the file `Makefile`
563 # and the LICENSE file from the parent directory
564 union ./Makefile ../LICENSE
565 ```
566
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 ]);
583
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)).
589
590 The given file sets are evaluated as lazily as possible,
591 with earlier elements being evaluated first if needed.
592
593
594 # Inputs
595
596 `filesets`
597
598 : A list of file sets. The elements can also be paths, which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
599
600 # Type
601
602 ```
603 unions :: [ FileSet ] -> FileSet
604 ```
605
606 # Examples
607 :::{.example}
608 ## `lib.fileset.unions` usage example
609
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
616
617 # Recursively include all files in the `src/code` directory
618 # If this directory is empty this has no effect
619 ./src/code
620
621 # Include the files `run.sh` and `unit.c` from the `tests` directory
622 ./tests/run.sh
623 ./tests/unit.c
624
625 # Include the `LICENSE` file from the parent directory
626 ../LICENSE
627 ]
628 ```
629
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 ];
647
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)).
651
652 The given file sets are evaluated as lazily as possible,
653 with the first argument being evaluated first if needed.
654
655
656 # Inputs
657
658 `fileset1`
659
660 : The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
661
662 `fileset2`
663
664 : The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
665
666 # Type
667
668 ```
669 intersection :: FileSet -> FileSet -> FileSet
670 ```
671
672 # Examples
673 :::{.example}
674 ## `lib.fileset.intersection` usage example
675
676 ```nix
677 # Limit the selected files to the ones in ./., so only ./src and ./Makefile
678 intersection ./. (unions [ ../LICENSE ./src ./Makefile ])
679 ```
680
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);
701
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).
705
706 The given file sets are evaluated as lazily as possible,
707 with the first argument being evaluated first if needed.
708
709
710 # Inputs
711
712 `positive`
713
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).
715
716 `negative`
717
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).
719
720 # Type
721
722 ```
723 union :: FileSet -> FileSet -> FileSet
724 ```
725
726 # Examples
727 :::{.example}
728 ## `lib.fileset.difference` usage example
729
730 ```nix
731 # Create a file set containing all files from the current directory,
732 # except ones under ./tests
733 difference ./. ./tests
734
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 ```
743
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);
764
765 /**
766 Filter a file set to only contain files matching some predicate.
767
768
769 # Inputs
770
771 `predicate`
772
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.
775
776 This function is called with an attribute set containing these attributes:
777
778 - `name` (String): The name of the file
779
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.
782
783 - `hasExt` (String -> Bool): Whether the file has a certain file extension.
784 `hasExt ext` is true only if `hasSuffix ".${ext}" name`.
785
786 This also means that e.g. for a file with name `.gitignore`,
787 `hasExt "gitignore"` is true.
788
789 Other attributes may be added in the future.
790
791 `path`
792
793 : The path whose files to filter
794
795 # Type
796
797 ```
798 fileFilter ::
799 ({
800 name :: String,
801 type :: String,
802 hasExt :: String -> Bool,
803 ...
804 } -> Bool)
805 -> Path
806 -> FileSet
807 ```
808
809 # Examples
810 :::{.example}
811 ## `lib.fileset.fileFilter` usage example
812
813 ```nix
814 # Include all regular `default.nix` files in the current directory
815 fileFilter (file: file.name == "default.nix") ./.
816
817 # Include all non-Nix files from the current directory
818 fileFilter (file: ! file.hasExt "nix") ./.
819
820 # Include all files that start with a "." in the current directory
821 fileFilter (file: hasPrefix "." file.name) ./.
822
823 # Include all regular files (not symlinks or others) in the current directory
824 fileFilter (file: file.type == "regular") ./.
825 ```
826
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;
848
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.
852
853 This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`.
854
855 A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource).
856
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 :::
861
862
863 # Inputs
864
865 `source`
866
867 : 1\. Function argument
868
869 # Type
870
871 ```
872 fromSource :: SourceLike -> FileSet
873 ```
874
875 # Examples
876 :::{.example}
877 ## `lib.fileset.fromSource` usage example
878
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 }
886
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
896
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 ```
905
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;
934
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.
937
938 This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults.
939
940
941 # Inputs
942
943 `path`
944
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.
947
948 # Type
949
950 ```
951 gitTracked :: Path -> FileSet
952 ```
953
954 # Examples
955 :::{.example}
956 ## `lib.fileset.gitTracked` usage example
957
958 ```nix
959 # Include all files tracked by the Git repository in the current directory
960 gitTracked ./.
961
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 ```
966
967 :::
968 */
969 gitTracked =
970 path:
971 _fromFetchGit
972 "gitTracked"
973 "argument"
974 path
975 {};
976
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.
981
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`.
986
987 If you don't need the configuration,
988 you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead.
989
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).
992
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).
997
998 This may change in the future.
999 :::
1000
1001
1002 # Inputs
1003
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`.
1008
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.
1012
1013 # Type
1014
1015 ```
1016 gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet
1017 ```
1018
1019 # Examples
1020 :::{.example}
1021 ## `lib.fileset.gitTrackedWith` usage example
1022
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 ```
1028
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 });
1051}