[0x01]:nix之路——一个软件包的艺术之旅
上一篇文章简单介绍了一下nix核心在做些什么,似乎有些空中楼阁。这次我希望能够通过一个具体的包,让你感受一下,为什么nix这个家伙即使在如此离经叛道的情况下,经过二十多年的发展,狂揽一大批忠实用户和开发者。 当然,你需要先安装nix,并开启nix-command以及flakes两个特性,因为篇幅问题,这部分我就不太赘述了,直接参考determinate systems的文档好了。 本文将带你以最快的速度理解nix的flake机制,准备好了吗? hello nix 在我们之前有关“究极构建系统”的设想中,将构建过程抽象成了一个函数,参数是构建依赖,返回值是构建结果。而nix实质上,就是一个函数式的构建系统,函数nix的一等公民。接收参数,返回结果: 有关上面的代码,你需要理解以下关键内容,更多有关nix语法的介绍可以参考nixos-cn的文档 声明了一个函数,以:为分割,前面为参数,后面为返回值 函数接收一个属性集作为参数,其中只有nixpkgs_path一个属性,当不传递任何参数时,改属性的默认值为fetchGit函数的返回结果 let … in是一个语法,作用是在一个在有限作用域内声明绑定关系,便于在后面反复使用 import关键字的作用是从一个路径导入函数,如果路径是一个目录,则导入default.nix nixpkgs是一个使用nix语言编写的代码仓库,其中包含了超过12万个包的构建代码 那这个函数做了什么呢?其实什么也没做什么复杂的事情: 使用git下载一个目录 将目录导入为函数,调用得到pkgs 返回pkgs中的hello 这个函数可以说是最简单的构建函数,什么也不做,只把构建参数直接返回。调用函数也很简单: 看我们的函数返回了一个什么,.drv?不要怕,如果你读过上一篇【nix之路——究极构建系统】,这个东西就是之前提及的.bld。 使用nix derivation show可以将这个文件转换为json查看: nixpkgs仓库比较大,下载需要多等待一会儿 当然,直接指定绝对路径也可以: 所以说,pkgs.hello实际上对应一个.drv文件,这个文件包含了能够构建这个包的所有信息。那我们如何最终构建他呢?也很简单: 构建完成后,默认会在当前目录建立一个指向产物目录的软连接,让我们可以快速使用 当然,以上只是为了让你更好的理解nix代码和产物的关系,实际上用下面的命令可以构建出一样的结果: hello flake.nix 也许你发现了,上面我们每次调用default.nix的时候,都要重新下载nixpkgs。而且,函数参数的默认值是一个下载函数,感觉哪里怪怪的。 其实nix提供了一种更好的的,导入外部nix代码依赖的机制,叫做flake,不同于default.nix,当指定一个目录时,flake.nix是默认的入口文件。他的格式也很简单: 这个文件核心在于inputs以及outputs两部分 inputs inputs声明了需要的外部nix代码仓库地址,地址可以是git仓库、本地路径、压缩包、压缩包下载链接等等 outputs outputs很显然是一个函数,就像上面我们写的default.nix一样,他接收一个属性集作为参数,属性来自inputs中的声明,最后返回一个属性集。 也许你注意到了,不同于我们自己写的default.nix,这里的参数多出了一个self属性,我们可以简单模拟一下flake.nix的执行过程: 等等等等,发生了什么?这里的fix函数其实是nix语言中的一个常用trick,称为fix point,既然你早晚要面对他,我决定在这里直接告诉你它的存在。 不过,我并不打算在这里过分展开fix point的原理,有关fix point的原理,我看过比较好的介绍是akavel的这篇文章,你可以选择去深入了解一下。 如果你暂时还不想知道fix干了什么,只需要知道它的效果就可以了,在这里我们尝试使用我们的call_flake.nix调用一下flake.nix,看看发生了什么: 可以看到,如果我们在ouptuts的返回值中导出self,这个self就不断指向返回结果自己。这样可以让你很方便的在outputs的实现中,引用自己对外导出的部分。 好了,小插曲到此为止,还是让我们看一下如何正常操作flake.nix吧: 首先,使用nix flake show可以查看flake中outputs函数的返回结果,但默认支持的类型有限,其他不支持展示的属性只能展示为unknown: 如果你想知道flake的详细返回内容是什么怎么办?你可以调用nix repl,使用:lf来调用flake.nix,获取返回值: nix_repl_flake 一个软件包的艺术之旅 我们现在知道flake机制的运作原理,以及如何将gnu hello作为packages.${system}.hello作为flake的返回值返回,那我们都可以对这个包做些什么操作呢? 首先,我们可以使用nix build快速构建一个flake仓库导出的包,构建后默认会在当前目录建立一个指向构建产物的软链接: 你也可以选择使用nix run直接运行它,运行前他也会检查是否构建过,如果没构建会先构建再执行: 也可以使用nix derivation show查看这个包对应的.drv文件: 你也可以将某个包,以及他的所有运行时依赖拷贝到别处,只需要一个内核就可以运行,可以使用chroot验证一下: 当然,nixpkgs本身也是一个flake仓库,nixpkgs仓库中任何包都可以用上面的方式操作: ...