Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document "Import From Derivation" #7332

Merged
merged 15 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/manual/src/SUMMARY.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- [Operators](language/operators.md)
- [Derivations](language/derivations.md)
- [Advanced Attributes](language/advanced-attributes.md)
- [Import From Derivation](language/import-from-derivation.md)
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved
- [Built-in Constants](language/builtin-constants.md)
- [Built-in Functions](language/builtins.md)
- [Advanced Topics](advanced-topics/advanced-topics.md)
Expand Down
5 changes: 4 additions & 1 deletion doc/manual/src/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,15 @@

- [store object]{#gloss-store-object}


A store object consists of a [file system object], [reference]s to other store objects, and other metadata.
It can be referred to by a [store path].

[store object]: #gloss-store-object

- [IFD]{#gloss-ifd}

[Import From Derivation](./language/import-from-derivation.md)

- [input-addressed store object]{#gloss-input-addressed-store-object}

A store object produced by building a
Expand Down
139 changes: 139 additions & 0 deletions doc/manual/src/language/import-from-derivation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Import From Derivation
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved

The value of a Nix expression can depend on the contents of a [store object].
In this case, when that store object is needed, evaluation will be paused, the store object [realised], and then evaluation resumed.
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved

[store object]: @docroot@/glossary.md#gloss-store-object
[derivation]: @docroot@/glossary.md#gloss-derivation
[realised]: @docroot@/glossary.md#gloss-realise

This has performance implications:
Since evaluation is sequential, each required store object that is not already in the store will also be realised sequentially.
Usually, if store objects are not already present, realisation is orders of magnitude slower than evaluation.
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved

Passing an expression `expr` that evaluates to a [store path](@docroot@/glossary.md#gloss-store-path) to any built-in function which reads from the filesystem constitutes Import From Derivation:

- [`import`](./builtins.md#builtins-import)` expr`
- [`builtins.readFile`](./builtins.md#builtins-readFile)` expr`
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved
- [`builtins.readFileType`](./builtins.md#builtins-readFileType)` expr`
- [`builtins.readDir`](./builtins.md#builtins-readDir)` expr`
- [`builtins.pathExists`](./builtins.md#builtins-pathExists)` expr`
- [`builtins.filterSource`](./builtins.md#builtins-filterSource)` f expr`
- [`builtins.path`](./builtins.md#builtins-path)` { path = expr; }`
- [`builtins.hashFile`](./builtins.md#builtins-hashFile)` t expr`
- `builtins.scopedImport x drv`

Realising store objects during evaluation can be disabled by setting [`allow-import-from-derivation`](../command-ref/conf-file.md#conf-allow-import-from-derivation) to `false`.
Without IFD it is ensured that evaluation is complete and Nix can produce a build plan before starting any realisation.
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved

## Example

In the following Nix expression, the inner derivation `drv` produces a file with contents `hello`.

```nix
# IFD.nix
let
drv = derivation {
name = "hello";
builder = "/bin/sh";
args = [ "-c" "echo -n hello > $out" ];
system = builtins.currentSystem;
};
in "${builtins.readFile drv} world"
```

```shellSession
nix-instantiate IFD.nix --eval --read-write-mode
```

```
building '/nix/store/348q1cal6sdgfxs8zqi9v8llrsn4kqkq-hello.drv'...
"hello world"
```

The contents of the derivation's [output path](@docroot@/glossary.md#gloss-output-path) have to be [realised] before they can be read with [`readFile`](./builtins.md#builtins-readFile).
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved
Only then evaluation can continue to produce the final result.

## Illustration

As a first approximation, the following data flow graph shows how evaluation and building are interleaved, if the value of a Nix expression depends on realising a store object.
Boxes are data structures, arrow labels are transformations.

```
+----------------------+ +------------------------+
| Nix evaluator | | Nix store |
| .----------------. | | |
| | Nix expression | | | |
| '----------------' | | |
| | | | |
| evaluate | | |
| | | | |
| V | | |
| .------------. | | .------------------. |
| | derivation |----|-instantiate-|->| store derivation | |
| '------------' | | '------------------' |
| | | | |
| | | realise |
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved
| | | | |
| | | V |
| .----------------. | | .--------------. |
| | Nix expression |<-|----read-----|----| store object | |
| '----------------' | | '--------------' |
| | | | |
| evaluate | | |
| | | | |
| V | | |
| .------------. | | |
| | value | | | |
| '------------' | | |
+----------------------+ +------------------------+
```

In more detail, the following sequence diagram shows how the expression is evaluated step by step, and where evaluation is blocked to wait for the build output to appear.

```
.-------. .-------------. .---------.
|Nix CLI| |Nix evaluator| |Nix store|
'-------' '-------------' '---------'
| | |
|evaluate IFD.nix| |
|--------------->| |
| | |
| evaluate `"${readFile drv} world"` |
| | |
| evaluate `readFile drv` |
| | |
| evaluate `drv` as string |
| | |
| |instantiate /nix/store/...-hello.drv|
| |----------------------------------->|
| : |
| : realise /nix/store/...-hello.drv |
| :----------------------------------->|
| : |
| |--------.
| : | |
| (evaluation blocked) | echo hello > $out
| : | |
| |<-------'
| : /nix/store/...-hello |
| |<-----------------------------------|
| | |
| resume `readFile /nix/store/...-hello` |
| | |
| | readFile /nix/store/...-hello |
| |----------------------------------->|
| | |
| | hello |
| |<-----------------------------------|
| | |
| resume `"${"hello"} world"` |
| | |
| resume `"hello world"` |
| | |
| "hello world" | |
|<---------------| |
.-------. .-------------. .---------.
|Nix CLI| |Nix evaluator| |Nix store|
'-------' '-------------' '---------'
```
11 changes: 6 additions & 5 deletions src/libexpr/eval-settings.hh
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ struct EvalSettings : Config
Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation",
R"(
By default, Nix allows you to `import` from a derivation, allowing
building at evaluation time. With this option set to false, Nix will
throw an error when evaluating an expression that uses this feature,
allowing users to ensure their evaluation will not require any
builds to take place.
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).

With this option set to `false`, Nix will throw an error when evaluating an expression that uses this feature,
even when the required store object is readily available.
This ensures that evaluation will not require any builds to take place,
regardless of the state of the store.
)"};

Setting<Strings> allowedUris{this, {}, "allowed-uris",
Expand Down
Loading