Skip to content

Commit f8c6e16

Browse files
committed
Updates
1 parent 836b6cd commit f8c6e16

1 file changed

Lines changed: 164 additions & 15 deletions

File tree

source/tutorials/source-file-selection.md

Lines changed: 164 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Source file selection
33
<!-- Note on title choice: While there's more uses outside of sources, it's by far the most prominent one -->
44

5+
<!-- TODO: Switch all mentions of unstable to stable once 23.11 is out -->
6+
57
To build a local project in a Nix derivation, its source files must be accessible to the builder.
68
But since the builder runs in an isolated environment (if the [sandbox](https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-sandbox) is enabled),
79
it won't have access to the local project files by default.
@@ -27,7 +29,6 @@ $ cd select
2729
$ nix-shell -p niv --run "niv init --nixpkgs nixos/nixpkgs --nixpkgs-branch nixos-unstable"
2830
```
2931

30-
<!-- TODO: Switch to 23.11 once out -->
3132
:::{note}
3233
For now we're using the nixos-unstable channel, since no stable channel has all the features we need yet.
3334
:::
@@ -46,7 +47,10 @@ let
4647
inherit system;
4748
};
4849
in
49-
pkgs.callPackage ./package.nix { }
50+
{
51+
package = pkgs.callPackage ./package.nix { };
52+
inherit pkgs;
53+
}
5054
```
5155

5256
In this tutorial we'll experiment with different `package.nix` contents, while keeping `default.nix` the same.
@@ -62,7 +66,7 @@ runCommand "hello" { } ''
6266

6367
And try it out:
6468
```shell-session
65-
$ nix-build
69+
$ nix-build -A package
6670
this derivation will be built:
6771
/nix/store/kmf9sw8fn7ps3ndqs31hvqwsa35b8l3g-hello.drv
6872
building '/nix/store/kmf9sw8fn7ps3ndqs31hvqwsa35b8l3g-hello.drv'...
@@ -76,7 +80,9 @@ but we'll omit that for the sake of the tutorial, since we only need the build l
7680
This also makes it easier to build it again, since successful derivation builds would get cached.
7781
From now on we'll also make build outputs a bit shorter for the sake of brevity.
7882

79-
## Builtin coercion of paths to strings
83+
## Builtins
84+
85+
### Coercion of paths to strings
8086

8187
The easiest way to use local files in builds is using the built-in coercion of [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) to strings.
8288

@@ -134,7 +140,7 @@ The two main ways to coerce paths to strings are:
134140

135141
These all do the same when built:
136142
```shell-session
137-
$ nix-build
143+
$ nix-build -A package
138144
building '/nix/store/9fi0khrkmqw5srjzjsfa0b05hf8div4c-file-coercion.drv'...
139145
++ cat /nix/store/j5lwpnlfrngks3bpidfr5hcrhgq0fy78-string.txt
140146
This is a string
@@ -149,7 +155,7 @@ $ nix-store --add $(pwd)/string.txt
149155
/nix/store/j5lwpnlfrngks3bpidfr5hcrhgq0fy78-string.txt
150156
```
151157

152-
## Built-in path coercion on directories
158+
### Coercion of directory paths to strings
153159

154160
This path coercion also works on directories the same as it does on files, let's try it out:
155161

@@ -165,7 +171,7 @@ runCommand "directory-coercion" {
165171

166172
Running it gives us:
167173
```shell-session
168-
$ nix-build
174+
$ nix-build -A package
169175
building '/nix/store/6ybg4v48xy8azhrnfdccdmhd2gr938f5-directory-interpolation.drv'...
170176
/nix/store/xdfchqpfx20ar9jil9kys99wc6hnm9zx-select
171177
|-- default.nix
@@ -195,7 +201,7 @@ Setting `src` will copy the resulting store path into the build directory and ma
195201
For the many commands that expect to be able to write to the current directory, this is great:
196202

197203
```shell-session
198-
$ nix-build
204+
$ nix-build -A package
199205
building '/nix/store/2cqd93fpnb4vqwkwmbl66dbxhndq1vhh-directory-coercion.drv'...
200206
unpacking sources
201207
unpacking source archive /nix/store/178fbwa8iwdl6b85yafksdbwlxf6mjca-select
@@ -228,7 +234,7 @@ However there are some subtle problems with this approach:
228234
- If you have any secrets in the current directory,
229235
they get imported into the Nix store too, exposing them to all users on the system!
230236

231-
## `builtins.path`
237+
### `builtins.path`
232238

233239
<!-- TODO: Use lib.cleanSourceWith instead and only briefly mention builtins.path? -->
234240

@@ -239,7 +245,7 @@ And it allows selecting the files that should be included with its `filter` argu
239245
```nix
240246
{ runCommand, tree, lib }:
241247
let
242-
source = builtins.path {
248+
src = builtins.path {
243249
# The convention is to use "source"
244250
name = "source";
245251
path = ./.;
@@ -251,17 +257,20 @@ let
251257
|| ! lib.hasSuffix ".nix" pathString;
252258
};
253259
in
254-
runCommand "builtins-path" {
260+
stdenv.mkDerivation {
261+
name = "builtins-path";
262+
inherit src;
255263
nativeBuildInputs = [ tree ];
256-
} ''
257-
tree ${source}
258-
''
264+
postInstall = ''
265+
tree
266+
'';
267+
}
259268
```
260269

261270
The shown filter will recurse into all directories and filter out all `.nix` files:
262271

263272
```shell-session
264-
$ nix-build
273+
$ nix-build -A package
265274
building '/nix/store/3x051rr6fainqi3a4mmmb06145m0j0mw-builtins-path.drv'...
266275
/nix/store/95mlqjmm13vd4ambw2pac5gj6i4wxcx4-source
267276
|-- nix
@@ -272,6 +281,146 @@ building '/nix/store/3x051rr6fainqi3a4mmmb06145m0j0mw-builtins-path.drv'...
272281
Writing more complex `filter` functions however is notoriously tricky,
273282
which is why it's not recommended to use this function directly.
274283

284+
## File sets
285+
286+
A better way to select local files is to use the [file set library](https://nixos.org/manual/nixpkgs/unstable/#sec-functions-library-fileset).
287+
This library is an abstraction built on top of `builtins.path`, with essentially the same functionality, but a much easier to use and safer interface.
288+
289+
The library is based on the concept of file sets,
290+
a data type representing a collection of local files.
291+
File sets can be created, composed and used with the various functions of the library.
292+
293+
The easiest way to experiment with the file set library is to first use it through `nix repl`.
294+
We can use the `pkgs` attribute exposed by `default.nix`
295+
to define `fs = pkgs.lib.fileset` as a shorthand alias for the library:
296+
297+
```shell-session
298+
$ nix repl -f .
299+
[...]
300+
nix-repl> fs = pkgs.lib.fileset
301+
```
302+
303+
### Basics
304+
305+
It's probably the easiest to just jump right in
306+
by using the [`trace`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fileset.trace) function,
307+
which pretty-prints the files included in a given file set:
308+
309+
```shell-session
310+
nix-repl> fs.trace ./. null
311+
trace: /home/user/select (all files in directory)
312+
null
313+
```
314+
315+
You might wonder where the file set here is, because we just passed a _path_ to the function!
316+
317+
The key is that for all file set functions that expect a file set for an argument, it also accepts a path instead.
318+
Such a path argument is then [implicitly coerced](https://nixos.org/manual/nixpkgs/unstable/#sec-fileset-path-coercion)
319+
to a file set that contains _all files under the given path_.
320+
We can see this from the trace `/home/user/select (all files in directory)`
321+
322+
Note that in contrast to [coercion of paths to strings](#builtin-coercion-of-paths-to-strings),
323+
file sets _never_ add their files to the Nix store unless explicitly requested.
324+
So you don't have to worry about accidentally copying secrets into the store.
325+
326+
:::{tip}
327+
The `trace` function pretty-prints its first agument and returns its second argument.
328+
But since you often just need the pretty-printing in `nix repl`, you can omit the second argument:
329+
330+
```shell-session
331+
nix-repl> fs.trace ./.
332+
trace: /home/user/select (all files in directory)
333+
«lambda @ /nix/store/1czr278x24s3bl6qdnifpvm5z03wfi2p-nixpkgs-src/lib/fileset/default.nix:555:8»
334+
```
335+
:::
336+
337+
This implicit coercion also works for files:
338+
339+
```shell-session
340+
nix-repl> fs.trace ./string.txt
341+
trace: /home/user/select
342+
trace: - string.txt (regular)
343+
```
344+
345+
We can see that in addition to the included file,
346+
it also prints its [file type](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType).
347+
348+
And if we make a typo for a path that doesn't exist, the file set library adequately complains about it:
349+
350+
```shell-session
351+
nix-repl> fs.trace ./string.nix
352+
error: lib.fileset.trace: Argument (/home/user/select/string.nix) is a path that does not exist.
353+
```
354+
355+
### Adding files to the Nix store
356+
357+
The file set library wouldn't be very useful if you couldn't also add its files to the Nix store for use in derivations.
358+
That's where [`toSource`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fileset.toSource) comes in.
359+
It allows us to create a Nix store path containing exactly the files in the file set, with its root at a specific path.
360+
361+
Let's switch back to editing our `package.nix` file to try it out:
362+
363+
```nix
364+
{ stdenv, tree, lib }:
365+
let
366+
fs = lib.fileset;
367+
in
368+
stdenv.mkDerivation {
369+
name = "filesets";
370+
src = fs.toSource {
371+
root = ./.;
372+
fileset = ./string.txt;
373+
};
374+
nativeBuildInputs = [ tree ];
375+
postInstall = ''
376+
tree
377+
'';
378+
}
379+
```
380+
381+
Building this, we can see that indeed only `string.nix` was included:
382+
```
383+
$ nix-build
384+
building '/nix/store/1cdgj8b1jrg2k751jidj9564r66lpvj9-filesets.drv'...
385+
unpacking sources
386+
unpacking source archive /nix/store/083k2phzhdakc649z5ql8f7cyisws6wp-source
387+
[...]
388+
.
389+
`-- string.txt
390+
```
391+
392+
We can also avoid using `tree` by using the `trace` function instead:
393+
394+
```nix
395+
{ stdenv, lib }:
396+
let
397+
fs = lib.fileset;
398+
sourceFiles = ./string.txt;
399+
in
400+
fs.trace sourceFiles
401+
stdenv.mkDerivation {
402+
name = "filesets";
403+
src = fs.toSource {
404+
root = ./.;
405+
fileset = sourceFiles;
406+
};
407+
}
408+
```
409+
410+
And building it:
411+
412+
```
413+
$ nix-build
414+
trace: /home/user/select
415+
trace: - string.txt (regular)
416+
```
417+
418+
### Combinators
419+
420+
The real benefit of the file set library lies in its combinator functions.
421+
422+
423+
275424
<!--
276425
277426
Mention lib.cleanSource, it's kind of the only function there's no good replacement for yet

0 commit comments

Comments
 (0)