• 来看一下Nix的hash部分如何求值

  • 先以单个文件为例 $ echo mycontent > myfile

    1
    2
    
    nix-repl> derivation { system = "x86_64-linux"; builder = ./myfile; name = "foo"; }
    «derivation /nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv»
    

    $ nix derivation show /nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    {
      "/nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv": {
        "args": [],
        "builder": "/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile",
        "env": {
          "builder": "/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile",
          "name": "foo",
          "out": "/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo",
          "system": "x86_64-linux"
        },
        "inputDrvs": {},
        "inputSrcs": [
          "/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"
        ],
        "name": "foo",
        "outputs": {
          "out": {
            "path": "/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo"
          }
        },
        "system": "x86_64-linux"
      }
    }
    

    (也可以用nix-store --add myfile来得到这个hash)

  • inputDrvs部分文件的hash部分的求值 其中/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile的hash部分是怎么求出的呢?

      1. 计算出文件的hash $ nix-hash --type sha256 myfile 2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3 ( 注:nix-hash做的事比普通的sha256sum要多一步,就是先把文件或目录打成NAR格式,以上面为例,其等于nix-store --dump | sha256sumnix-hash --type sha256 --flat myfilesha256sum myfile是等价的 对于Nix来说,只有两种内容格式,flat for regular files, or recursive for NAR serializations which can be anything. )
      1. 创建字符描述 $ echo -n "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile" > myfile.str
      1. 计算出最后的hash $ nix-hash --type sha256 --truncate --base32 --flat myfile.str xv2iccirbrvklck36f1g7vldn5v58vck
  • out pathhash部分的求值 之前我们说过,out path在derivation实际被构建之间就已知了,而且只与输入有关 out path的hash求值与输入类似,除了类型变为了output:out(多个输出则类型分别为output:<id>)

      1. 获取.drv文件中除了out path部分的内容的hash
      1
      2
      
      cp -f /nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv myout.drv
      sed -i 's,/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo,,g' myout.drv
      

      ( 这么做有点hack,实际上out path的求值过程中应该是有个中间文件,其内容类似如下(全在一行,没有换行):

      1
      
      Derive([("out","","","")],[],["/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"],"x86_64-linux","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile",[],[("builder","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"),("name","foo"),("out",""),("system","x86_64-linux")])
      

      out内容为空的.drv文件的内容 )

      求hash $ sha256sum myout.drv 1bdc41b9649a0d59f270a92d69ce6b5af0bc82b46cb9d9441ebc6620665f40b5 myout.drv

      1. 创建字符串描述 $ echo -n "output:out:sha256:1bdc41b9649a0d59f270a92d69ce6b5af0bc82b46cb9d9441ebc6620665f40b5:/nix/store:foo" > myout.str
      1. 对字符串描述文件求hash,得到最后的hash $ nix-hash --type sha256 --truncate --base32 --flat myout.str hs0yi5n5nw6micqhy8l1igkbhqdkzqa1
  • tar包采用一种特别的求值方式,其只与文件的内容有关,而与文件名无关(fixed-output paths),但步骤类似

    • derivation有三个特别的和hash相关的参数: outputHashMode outputHash outputHashAlgo,分别表示该derivation的hash的模式(“flat” or “recursive”),hash值和hash的算法 (refer: outputHashMode)

    • 当指定了outputHash时,derivation函数会确保该derivation的out path的hash部分的值为outputHash的值

    • 以之前的tar文件hello-2.12.1.tar.gz为例 $ sha256sum hello-2.12.1.tar.gz的到hash8d99142afd92576f30b0cd7cb42a8dc6809998bc5d607d88761f512e26c7db20 $ nix-instantiate --expr 'derivation { system = "x86_64-linux"; name = "helloTar"; builder = "none"; outputHash = "8d99142afd92576f30b0cd7cb42a8dc6809998bc5d607d88761f512e26c7db20"; outputHashMode = "flat"; outputHashAlgo = "sha256"; }'得到derivation /nix/store/gszqyzlnns85sjy1rj9jg04kil5fl39w-helloTar.drv

    通过nix derivation show /nix/store/gszqyzlnns85sjy1rj9jg04kil5fl39w-helloTar.drv 查看derivation的详细信息

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    {
      "/nix/store/gszqyzlnns85sjy1rj9jg04kil5fl39w-helloTar.drv": {
        "args": [],
        "builder": "none",
        "env": {
          "builder": "none",
          "name": "helloTar",
          "out": "/nix/store/qwj2km5i1p31616kmxgkm9iinfxs7iqr-helloTar",
          "outputHash": "8d99142afd92576f30b0cd7cb42a8dc6809998bc5d607d88761f512e26c7db20",
          "outputHashAlgo": "sha256",
          "outputHashMode": "flat",
          "system": "x86_64-linux"
        },
        "inputDrvs": {},
        "inputSrcs": [],
        "name": "helloTar",
        "outputs": {
          "out": {
            "hash": "8d99142afd92576f30b0cd7cb42a8dc6809998bc5d607d88761f512e26c7db20",
            "hashAlgo": "sha256",
            "path": "/nix/store/qwj2km5i1p31616kmxgkm9iinfxs7iqr-helloTar"
          }
        },
        "system": "x86_64-linux"
      }
    }
    

    然后像以前一样,把描述性的字符放入文件,然后对其求hash $ echo -n 'fixed:out:sha256:8d99142afd92576f30b0cd7cb42a8dc6809998bc5d607d88761f512e26c7db20:' > helloTar.str sha256sum helloTar.str 2dd22467c73f65de429fd32c70e68444aeb55f502082f23f8d509185e0341c22 helloTar.str

    比inputSrc和out path多一步,再用结果放入文件求一次hash $ echo -n "output:out:sha256:2dd22467c73f65de429fd32c70e68444aeb55f502082f23f8d509185e0341c22:/nix/store:helloTar" > helloTar.res $ nix-hash --type sha256 --truncate --base32 --flat helloTar.res qwj2km5i1p31616kmxgkm9iinfxs7iqr 哈哈,这正是我们的out path的hash部分

  • 总结 hash的计算总共有两次hash 对于输入文件(inputSrcs)来讲,其依赖于文件内容和文件名 先把用NAR的格式归档的文件本身hash 将hash即一些元数据以字符串的形式存在文件中 对该文件再次hash 对于输出文件(out path)来说,其hash只依赖输入(.drv文件),而与输出的内容无关 先将.drv文件(残缺"out" 部分的版本)求NAR hash 然后把hash与"output:“等源信息以字符串形式写入文件 最后对该文件求一次nix-hash --type sha256 --truncate --base32 --flat得到最后的hash tar文件与out path类似,只不过其不依赖.drv文件,而只依赖tar文件内容和derivation name等