Import From Derivation: Difference between revisions
imported>Roberth No edit summary |
imported>Roberth m Use (f drv) to clarify that it's only such usages that cause IFD |
||
Line 77: | Line 77: | ||
<h2>Recognizing IFD</h2> | <h2>Recognizing IFD</h2> | ||
The textbook case of IFD looks like `import | The textbook case of IFD looks like `import (f drv)` where `f drv` is an expression that requires a build in order to return a value. However, `import` is not the only function that can require a build. For example, `builtins.readFile` can have the same effect. | ||
All functions that query the filesystem will require a build when invoked on a derivation output path or subpath. This has the same effect and is therefore also called IFD. These include | All functions that query the filesystem will require a build when invoked on a derivation output path or subpath. This has the same effect and is therefore also called IFD. These include | ||
* <code>import</code> | * <code>import (f drv)</code> | ||
* <code>builtins.readFile</code> | * <code>builtins.readFile (f drv)</code> | ||
* <code>builtins.readDir</code> | * <code>builtins.readDir (f drv)</code> | ||
* <code>builtins.pathExists</code> | * <code>builtins.pathExists (f drv)</code> | ||
* <code>builtins.filterSource</code> | * <code>builtins.filterSource g (f drv) </code> | ||
* <code>builtins.path</code> | * <code>builtins.path { path = f drv; }</code> | ||
* <code>builtins.hashFile</code> | * <code>builtins.hashFile t (f drv)</code> | ||
* <code>builtins.scopedImport</code> | * <code>builtins.scopedImport x (f drv)</code> | ||
* potentially other functions if those read the filesystem in any way | * potentially other functions if those read the filesystem in any way | ||
* '''library functions''' that invoke the above, such as | * '''library functions''' that invoke the above, such as | ||
* <code>lib.cleanSource</code> | * <code>lib.cleanSource { src = f drv; }</code> | ||
* ... | * ... |
Revision as of 19:06, 11 November 2020
Import From Derivation (IFD) is where during a single Nix evaluation, the Nix expression:
- creates a derivation which will build a Nix expression
- imports that expression
- uses the results of the evaluation of the expression.
An example of IFD is:
let
pkgs = import <nixpkgs> {};
# Create a derivation which, when built, writes some Nix code to
# its $out path.
derivation-to-import = pkgs.writeText "example" ''
pkgs: {
ifd-example = pkgs.stdenv.mkDerivation rec {
name = "hello-2.10-ifd-example";
src = pkgs.fetchurl {
url = "mirror://gnu/hello/2.10.tar.gz";
sha256 = "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i";
};
};
}
'';
# Import the derivation. This forces `derivation-to-import` to become
# a string. This is normal behavior for Nix and Nixpkgs. The specific
# difference here is the evaluation itself requires the result to be
# built during the evaluation in order to continue evaluating.
imported-derivation = import derivation-to-import;
# Treat the imported-derivation variable as if we hadn't just created
# its Nix expression inside this same evaluation.
hello-package = (imported-derivation pkgs).ifd-example;
in hello-package
Building this looks familiar, but with an extra building ...
line:
$ nix-build ./test.nix building '/nix/store/8n001pyx2iqsnzd6niji1bvyjlg6x058-example.drv'... <- this build is forced at evaluation time these derivations will be built: <- now we're back to normal nix-build behavior /nix/store/3nm9rlv5smmvijcdifngjwl4v6zvll7k-hello-2.10-ifd-example.drv building '/nix/store/3nm9rlv5smmvijcdifngjwl4v6zvll7k-hello-2.10-ifd-example.drv'... [...snip...]
we'll see pretty similar output if we just evaluate it:
$ nix-instantiate ./test.nix building '/nix/store/8n001pyx2iqsnzd6niji1bvyjlg6x058-example.drv'... /nix/store/3nm9rlv5smmvijcdifngjwl4v6zvll7k-hello-2.10-ifd-example.drv
Some examples of IFD can be seen when using nixpkgs to fetch a specific version of nixpkgs, and then importing the source.
When to use IFD
IFD is a powerful feature, but it should be avoided _if possible_. IFD extends the duration of evaluation, which is single threaded and it affects the predictions of the `nix` progress bar.
Acceptable uses of IFD include importing a pinned nixpkgs and automation around lock files. Such uses vastly improve your development workflow, outweighing the slight disadvantages of IFD.
As a rule of thumb, if you can avoid IFD by refactoring Nix code or moving your build logic into the derivations themselves, you should do so.
Recognizing IFD
The textbook case of IFD looks like `import (f drv)` where `f drv` is an expression that requires a build in order to return a value. However, `import` is not the only function that can require a build. For example, `builtins.readFile` can have the same effect.
All functions that query the filesystem will require a build when invoked on a derivation output path or subpath. This has the same effect and is therefore also called IFD. These include
import (f drv)
builtins.readFile (f drv)
builtins.readDir (f drv)
builtins.pathExists (f drv)
builtins.filterSource g (f drv)
builtins.path { path = f drv; }
builtins.hashFile t (f drv)
builtins.scopedImport x (f drv)
- potentially other functions if those read the filesystem in any way
- library functions that invoke the above, such as
lib.cleanSource { src = f drv; }
- ...