2023-08-16 22:55:32 +00:00
{ lib }:
let
inherit ( import ./internal.nix { inherit lib ; } )
_coerce
2023-09-13 21:29:28 +00:00
_coerceMany
2023-08-16 22:55:32 +00:00
_toSourceFilter
2023-09-13 21:29:28 +00:00
_unionMany
2023-08-16 22:55:32 +00:00
;
inherit ( builtins )
2023-09-13 21:29:57 +00:00
isList
2023-08-16 22:55:32 +00:00
isPath
pathExists
typeOf
;
2023-09-13 21:29:57 +00:00
inherit ( lib . lists )
imap0
;
2023-08-16 22:55:32 +00:00
inherit ( lib . path )
hasPrefix
splitRoot
;
inherit ( lib . strings )
isStringLike
;
inherit ( lib . filesystem )
pathType
;
inherit ( lib . sources )
cleanSourceWith
;
in {
/*
Add the local files contained in ` fileset ` to the store as a single [ store path ] ( https://nixos.org/manual/nix/stable/glossary #gloss-store-path) rooted at `root`.
The result is the store path as a string-like value , making it usable e . g . as the ` src ` of a derivation , or in string interpolation :
` ` ` nix
stdenv . mkDerivation {
src = lib . fileset . toSource { . . . } ;
# ...
}
` ` `
The name of the store path is always ` source ` .
Type :
toSource : : {
root : : Path ,
fileset : : FileSet ,
} -> SourceLike
Example :
# Import the current directory into the store but only include files under ./src
toSource { root = ./. ; fileset = ./src ; }
= > " / n i x / s t o r e / . . . - s o u r c e "
# The file set coerced from path ./bar could contain files outside the root ./foo, which is not allowed
toSource { root = ./foo ; fileset = ./bar ; }
= > <error>
# The root has to be a local filesystem path
toSource { root = " / n i x / s t o r e / . . . - s o u r c e " ; fileset = ./. ; }
= > <error>
* /
toSource = {
/*
( required ) The local directory [ path ] ( https://nixos.org/manual/nix/stable/language/values.html #type-path) that will correspond to the root of the resulting store path.
Paths in [ strings ] ( https://nixos.org/manual/nix/stable/language/values.html #type-string), including Nix store paths, cannot be passed as `root`.
` root ` has to be a directory .
< ! - - Ignore the indentation here , this is a nixdoc rendering bug that needs to be fixed - ->
: : : { . note }
Changing ` root ` only affects the directory structure of the resulting store path , it does not change which files are added to the store .
The only way to change which files get added to the store is by changing the ` fileset ` attribute .
: : :
* /
root ,
/*
( required ) The file set whose files to import into the store .
Currently the only way to construct file sets is using [ implicit coercion from paths ] ( #sec-fileset-path-coercion).
If a directory does not recursively contain any file , it is omitted from the store path contents .
* /
fileset ,
} :
let
# We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement
# For now filesets are always paths
filesetPath = fileset ;
in
let
fileset = _coerce " l i b . f i l e s e t . t o S o u r c e : ` f i l e s e t ` " filesetPath ;
rootFilesystemRoot = ( splitRoot root ) . root ;
filesetFilesystemRoot = ( splitRoot fileset . _internalBase ) . root ;
2023-09-13 20:26:16 +00:00
filter = _toSourceFilter fileset ;
2023-08-16 22:55:32 +00:00
in
if ! isPath root then
if isStringLike root then
throw ''
lib . fileset . toSource : ` root ` " ${ toString root } " is a string-like value , but it should be a path instead .
Paths in strings are not supported by ` lib . fileset ` , use ` lib . sources ` or derivations instead . ''
else
throw ''
lib . fileset . toSource : ` root ` is of type $ { typeOf root } , but it should be a path instead . ''
# Currently all Nix paths have the same filesystem root, but this could change in the future.
# See also ../path/README.md
else if rootFilesystemRoot != filesetFilesystemRoot then
throw ''
lib . fileset . toSource : Filesystem roots are not the same for ` fileset ` and ` root ` " ${ toString root } " :
` root ` : root " ${ toString rootFilesystemRoot } "
` fileset ` : root " ${ toString filesetFilesystemRoot } "
Different roots are not supported . ''
else if ! pathExists root then
throw ''
lib . fileset . toSource : ` root ` $ { toString root } does not exist . ''
else if pathType root != " d i r e c t o r y " then
throw ''
lib . fileset . toSource : ` root ` $ { toString root } is a file , but it should be a directory instead . Potential solutions :
- If you want to import the file into the store _without_ a containing directory , use string interpolation or ` builtins . path ` instead of this function .
- If you want to import the file into the store _with_ a containing directory , set ` root ` to the containing directory , such as $ { toString ( dirOf root ) } , and set ` fileset ` to the file path . ''
else if ! hasPrefix root fileset . _internalBase then
throw ''
lib . fileset . toSource : ` fileset ` could contain files in $ { toString fileset . _internalBase } , which is not under the ` root ` $ { toString root } . Potential solutions :
- Set ` root ` to $ { toString fileset . _internalBase } or any directory higher up . This changes the layout of the resulting store path .
- Set ` fileset ` to a file set that cannot contain files outside the ` root ` $ { toString root } . This could change the files included in the result . ''
else
2023-09-13 20:26:16 +00:00
builtins . seq filter
2023-08-16 22:55:32 +00:00
cleanSourceWith {
name = " s o u r c e " ;
src = root ;
2023-09-13 20:26:16 +00:00
inherit filter ;
2023-08-16 22:55:32 +00:00
} ;
2023-09-13 21:29:28 +00:00
/*
The file set containing all files that are in either of two given file sets .
2023-09-13 21:29:57 +00:00
This is the same as [ ` unions ` ] ( #function-library-lib.fileset.unions),
but takes just two file sets instead of a list .
2023-09-13 21:29:28 +00:00
See also [ Union ( set theory ) ] ( https://en.wikipedia.org/wiki/Union_ ( set_theory ) ) .
The given file sets are evaluated as lazily as possible ,
with the first argument being evaluated first if needed .
Type :
union : : FileSet -> FileSet -> FileSet
Example :
# Create a file set containing the file `Makefile`
# and all files recursively in the `src` directory
union ./Makefile ./src
# Create a file set containing the file `Makefile`
# and the LICENSE file from the parent directory
union ./Makefile ../LICENSE
* /
union =
# The first file set.
# This argument can also be a path,
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
fileset1 :
# The second file set.
# This argument can also be a path,
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
fileset2 :
let
filesets = _coerceMany " l i b . f i l e s e t . u n i o n " [
{
context = " f i r s t a r g u m e n t " ;
value = fileset1 ;
}
{
context = " s e c o n d a r g u m e n t " ;
value = fileset2 ;
}
] ;
in
_unionMany filesets ;
2023-09-13 21:29:57 +00:00
/*
The file set containing all files that are in any of the given file sets .
This is the same as [ ` union ` ] ( #function-library-lib.fileset.unions),
but takes a list of file sets instead of just two .
See also [ Union ( set theory ) ] ( https://en.wikipedia.org/wiki/Union_ ( set_theory ) ) .
The given file sets are evaluated as lazily as possible ,
with earlier elements being evaluated first if needed .
Type :
unions : : [ FileSet ] -> FileSet
Example :
# Create a file set containing selected files
unions [
# Include the single file `Makefile` in the current directory
# This errors if the file doesn't exist
./Makefile
# Recursively include all files in the `src/code` directory
# If this directory is empty this has no effect
./src/code
# Include the files `run.sh` and `unit.c` from the `tests` directory
./tests/run.sh
./tests/unit.c
# Include the `LICENSE` file from the parent directory
../LICENSE
]
* /
unions =
# A list of file sets.
# Must contain at least 1 element.
# The elements can also be paths,
# which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
filesets :
let
# We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement
maybeFilesets = filesets ;
in
let
# Annotate the elements with context, used by _coerceMany for better errors
annotated = imap0 ( i : el : {
context = " e l e m e n t ${ toString i } o f t h e a r g u m e n t " ;
value = el ;
} ) maybeFilesets ;
filesets = _coerceMany " l i b . f i l e s e t . u n i o n s " annotated ;
in
if ! isList maybeFilesets then
throw " l i b . f i l e s e t . u n i o n s : E x p e c t e d a r g u m e n t t o b e a l i s t , b u t g o t a ${ typeOf maybeFilesets } . "
else if maybeFilesets == [ ] then
# TODO: This could be supported, but requires an extra internal representation for the empty file set
throw " l i b . f i l e s e t . u n i o n s : E x p e c t e d a r g u m e n t t o b e a l i s t w i t h a t l e a s t o n e e l e m e n t , b u t i t c o n t a i n s n o e l e m e n t s . "
else
_unionMany filesets ;
2023-08-16 22:55:32 +00:00
}