Nix目前的构建和发布模型
derivation outputs and output paths
Nix软件构建以Derivation为核心,Derivation有以下核心属性:
- Derivation表示了软件的构建过程;
- Derivation可以依赖其他Derivation;
- Derivation执行后具有副作用,会在
/nix/store下生成路径为xxx-name.drv的文件,其中xxx为该Derivation的唯一标识符,在任何电脑中都一致; xxx-name.drv可以被进一步构建成产物,并且产物的路径存储在xxx-name.drv中,在构建前就可以知道,此处我们称为outPath;outPath路径中也存在唯一标识,同一个Derrvation产生的.drv文件中的outPath一定相同;- 由于构建前就知道产物路径,引用某个derivation时,只要检查其中存储的
outPath存不存在,就可以判定需不需要重复编译; - 不同机器可以通过共享
outPath来共享构建缓存;
这里存在两个目录,一个是
.drv的目录,一个是outPath的目录,两者的路径中都有唯一标识符,且路径都根据Derivation的参数计算得来,outPath存储在.drv中。
构建过程
nix包管理的软件构建过程用伪码表示大致如下:
derivation = getDerivationFrom(nixpkgs, package_name)
drv = derivatoin() # 此处存在副作用,derivation函数调用后会生成.drv文件
if checkOutPathExist(drv.outPath):
pass # 如果产物目录已经存在,就不用重复构建了
else:
drv.build() # drv.build()会将软件构建后放到drv.outPath中
binary cache引入后:
derivation = getDerivationFrom(nixpkgs, package_name)
drv = derivatoin()
if checkOutPathExist(drv.outPath):
pass
else:
if fetchOutPathFromBinaryCache(drv.outPath):
pass # 尝试从BinaryCache服务器中下载对应的包,放到outPath中
else:
drv.build()
Input-Address Model
总体来说,上面我们描述的目前Nix的做法是将软件构建软件的过程(包括输入)抽象为一个哈希值,这个唯一哈希值可以代表软件的某个特定状态,然后用这个哈希值来索引构建后的软件。
在构建过程当中,任何输入的改变必然会导致输出的路径发生变化:
过度重复构建
你可能已经意识到了一些不对,在正常的软件构建过程当中,输入的改变不一定会导致输出不同,比如说代码路径下面增加了一些文档,或者说构建流程发生了改变。
然而在Nix目前的构建模型中,输入的任何变化都会导致输出路径的变化,但现实当中这样往往会导致一些不好的副作用。
例如go语言的源码中使用perl来做代码单元测试,原则上来说,使用什么版本的perl做单元测试,或者是否做单元测试,都不会改变构建产物。我们可以认为,源码中不是所有改动都会影响产物。但在nix中,如果perl包发生了改变,而go又依赖perl做单元测试,那么go的产物路径就会发生变化,进而需要重新编译。再进一步,所有依赖go的包也需要重新编译,导致了很多没有必要的重复编译。
Content-Address Model
解决上述问题的方式思路也很简单,就是使用content address的方式来确定构建缓存的实际目录。
所谓content address,与input address相对。在input address的模型中,用于索引value的key与value内容无关; 但在content address的模型中,用于索引value的key是根据value的内容计算得来。content address最简单的例子就是使用文件的哈希值来索引文件,给定文件的哈希值,返回文件内容; 而input address典型例子就是给定文件的文件名,返回文件的内容;
content address最经典的应用场景莫过于git,git本质上就是一个content addressable filesystem
早在nix最开始的260多页的论文中,作者就提到了content address的方案,并且例举了若干优点 包括信任模型的简化、重复构建的避免等等。
更进一步的,基于content address的模型,nix未来甚至可以引入例如IPFS之类的存储后端,从而做到分布式缓存,这也是nix项目下一步的主要方向。目前nix初步实现了Content Address Derivation,但还在早起阶段,hydra并没有进行默认集成。另外git hashing的支持也在路上,目前也有了初步实现。详细内容可以看下面的参考文章;
References
Nix Content Address Implementation
TOWARDS A CONTENT-ADDRESSED MODEL FOR NIX
SELF-REFERENCES IN A CONTENT-ADDRESSED NIX
DERIVATION OUTPUTS IN A CONTENT-ADDRESSED WORLD
IMPLEMENTING A CONTENT-ADDRESSED NIX