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