Import From Derivation: Difference between revisions

From NixOS Wiki
imported>Roberth
m Use (f drv) to clarify that it's only such usages that cause IFD
No edit summary
 
(8 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Import From Derivation (IFD) is where during a single Nix evaluation, the
See the Nix manual: [https://nixos.org/manual/nix/unstable/language/import-from-derivation Import From Derivation]
Nix expression:


# creates a derivation which will build a Nix expression
== Further reading ==
# imports that expression
# uses the results of the evaluation of the expression.


An example of IFD is:
* https://fzakaria.com/2020/10/20/nix-parallelism-import-from-derivation.html


<syntaxHighlight lang=nix>
[[Category: Nix]]
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
</syntaxHighlight>
 
Building this looks familiar, but with an extra <code>building ...</code> line:
 
{{Commands|<nowiki>
$ 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...]
</nowiki>}}
 
we'll see pretty similar output if we just evaluate it:
 
{{Commands|<nowiki>
$ nix-instantiate ./test.nix
building '/nix/store/8n001pyx2iqsnzd6niji1bvyjlg6x058-example.drv'...
 
/nix/store/3nm9rlv5smmvijcdifngjwl4v6zvll7k-hello-2.10-ifd-example.drv
</nowiki>}}
 
Some examples of IFD can be seen when using nixpkgs to fetch a specific
version of nixpkgs, and then importing the source.
 
<h2>When to use IFD</h2>
 
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.
 
<h2>Recognizing IFD</h2>
 
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
 
* <code>import (f drv)</code>
* <code>builtins.readFile (f drv)</code>
* <code>builtins.readDir (f drv)</code>
* <code>builtins.pathExists (f drv)</code>
* <code>builtins.filterSource g (f drv) </code>
* <code>builtins.path { path = f drv; }</code>
* <code>builtins.hashFile t (f drv)</code>
* <code>builtins.scopedImport x (f drv)</code>
* potentially other functions if those read the filesystem in any way
* '''library functions''' that invoke the above, such as
* <code>lib.cleanSource { src = f drv; }</code>
* ...

Latest revision as of 09:57, 13 September 2024