在Nix中的构建依赖与运行时依赖

27

我刚开始学习Nix,如果我在文档中错过了对我的问题的回答,请谅解。

我想使用Nix设置一个安全的生产环境,只包含最少量的库和可执行文件。因为这些可能会存在安全风险,所以我不想要任何编译器或其他构建工具。

当我安装一些软件包时,似乎它们仅依赖于最少量的运行时依赖项。例如,如果我安装apache-tomcat-8.0.23,那么我将得到Java运行时(JRE)和组成Tomcat的预编译JAR文件。

另一方面,有些软件包似乎在其依赖项中包括完整的构建工具链。以另一个基于Java的例子为例,当我安装spark-1.4.0时,Nix会拉取包含编译器的Java开发工具包(JDK),还会下载Maven构建工具等。

因此,我的问题如下:

  1. Nix软件包是否区分构建和运行时依赖关系?
  2. 为什么某些软件包似乎依赖于构建工具,而其他软件包仅需要运行时?这是否完全取决于软件包作者如何打包应用程序?
  3. 如果一个软件包包含我不想要的构建依赖项,作为操作者,除了设计自己的替代包装方式之外,我还能做些什么吗?

非常感谢。

1个回答

39
  1. 运行时依赖是 Nix 自动扫描生成的输出文件中每个构建时依赖存储路径哈希部分的一个子集。例如,如果使用编译器 /nix/store/abcdef...-foo-1.20 构建一个软件包,那么 Nix 将扫描生成的所有文件以查找哈希位abcdef...。如果找到该哈希,则假定该输出在某种程度上与编译器相关,因此将其作为运行时依赖项保留。但是,如果未出现该哈希值,则生成的输出不引用编译器,因此无法在运行时访问它,因此将foo-1.20视为仅限构建时的依赖项。

  2. 一些软件包为了信息和调试目的记录了大量构建环境的细节。例如 Perl 存储了有关编译它所使用的工具的每个细节,因此所有这些存储路径最终都被视为运行时依赖项,尽管 Perl 实际上在运行时并不需要它们,但 Nix 不知道这一点:它只知道 Perl 存储路径引用了这些工具。现在,Nixpkgs 的维护者通常会努力清理它,即通过从安装中删除包含所有这些存储路径的日志文件等方式,但肯定还有很多软件包在数据库中尚未针对此进行优化。

  3. 假设你想编译一个不依赖于 PAM 的openssh版本。然后,您可以通过覆盖的方式从表达式中删除构建输入,即通过将通常传递给openssh构建函数的pam参数替换为null。要执行此操作,请将以下文件存储在~/.nixpkgs/config.nix中:

{
  packageOverrides = super: let self = super.pkgs; in {
    openssh-without-pam = super.openssh.override {
      pam = null;
    };
  };
}

现在运行以下命令安装该软件包:

$ nix-env -f "<nixpkgs>" -iA openssh-without-pam

感谢您提供如此详尽和全面的答案。 - Neil Bartlett
你提到“通过扫描生成的输出自动执行”,Nix是如何实现这一点的?它是否可以处理二进制文件内部通过exec动态执行的路径?或者是通过生成的配置文件存储另一个软件包二进制文件的路径? - CMCDragonkai
@CMCDragonkai,是的,看起来nix实际上会扫描二进制派生哈希值。不幸的是,我找不到实际的代码,但这里有一些细节https://lethalman.blogspot.ru/2014/08/nix-pill-9-automatic-runtime.html - cvb
3
源参考 :) - linuxhackerman
1
我发现这个自动扫描对于shell脚本不起作用。因此,如果您的构建生成了一个shell脚本,您需要以某种方式进行包装,以了解PATH(例如makeWrapper),或者显式地替换可执行调用。 - CMCDragonkai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接