Evaluation order in a nix expression

If I instantiate this derivation:

let
  pkgs = import <nixpkgs> {};
in
derivation {
  name = "a";
  builder = "${pkgs.coreutils}/bin/true";
  system = builtins.currentSystem;
}

I get this drv file:

{
  "/nix/store/8axn61inlpba8vlvqwzjkn7pjdkmvpna-a.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/jlh3n04j9qglglvag58lz39cmsjx9ikw-a"
      }
    },
    "inputSrcs": [],
    "inputDrvs": {
      "/nix/store/nsncr60fii4fwki70bb5ishf1hzq89b5-coreutils-8.31.drv": [
        "out"
      ]
    },
    "platform": "x86_64-linux",
    "builder": "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true",
    "args": [],
    "env": {
      "builder": "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true",
      "name": "a",
      "out": "/nix/store/jlh3n04j9qglglvag58lz39cmsjx9ikw-a",
      "system": "x86_64-linux"
    }
  }
}

The string "${pkgs.coreutils}/bin/true" evaluates to "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true" on my system. So I thought that nix-build searches the derivation and registers paths inside nix store as inputDrvs. So I manually performed this replacement and got the following expression and drv:

let
  pkgs = import <nixpkgs> {};
in
derivation {
  name = "a";
  builder = "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true";
  system = builtins.currentSystem;
}
{
  "/nix/store/1psxqsh1w61pf2010xdjvdd8j1r1ddzx-a.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/slv8x7rv9zf60azzkwbkk4xasdffdr1h-a"
      }
    },
    "inputSrcs": [],
    "inputDrvs": {},
    "platform": "x86_64-linux",
    "builder": "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true",
    "args": [],
    "env": {
      "builder": "/nix/store/3kqc2wmvf1jkqb2jmcm7rvd9lf4345ra-coreutils-8.31/bin/true",
      "name": "a",
      "out": "/nix/store/slv8x7rv9zf60azzkwbkk4xasdffdr1h-a",
      "system": "x86_64-linux"
    }
  }
}

As you can see, this does not have coreutils in inputDrvs. This would not be the case if nix-build were evaluating the expression in argument first order. What is the reason behind this behaviour?

string contexts will track from what derivation an input came from. See more at Alternative language - #4 by zimbatm

1 Like

More nuances about string context: https://shealevy.com/blog/2018/08/05/understanding-nixs-string-context/

There are 2 main function used to manage string context

1

$ nix repl '<nixpkgs>'
nix-repl> builtins.getContext "${bash}/bin/bash -c '${python}/bin/python || ${coreutils}/bin/true"
{ "/nix/store/64cbsxyc5zpkdynz2r2szl02nx2nzc3l-coreutils-8.31.drv" = { ... }; 
  "/nix/store/8kzygjhk49lc6ycknziaqiw9rgvjr3c6-bash-4.4-p23.drv" = { ... }; 
  "/nix/store/xh1zpxvjmq9xsgj7ln30q8z3g7f0mbg3-python-2.7.17.drv" = { ... }; 
}

2

$ nix repl '<nixpkgs>'

nix-repl> (builtins.unsafeDiscardStringContext "${gcc}/bin/gcc") == "${gcc}/bin/gcc"
true

nix-repl> builtins.getContext (builtins.unsafeDiscardStringContext "${gcc}/bin/gcc") == builtins.getContext "${gcc}/bin/gcc"
false

Nix indeed does search /nix/store for references, however it does this during finalization of build to remove extra references, so only runtime references are left. It won’t add any new refs except those already present in string context.