什么是 Nix 构建表达式中的"patchShebangs"命令



在查看 Nixpkgs 存储库中的软件包时遇到了patchShebangs命令,并看到它在标准环境的通用构建器的各个阶段使用,但不确定它的用途或为什么首先需要它。

简而言之:在 Nix 构建期间使用的 shell 脚本无法开箱即用,因为 Nix 会清除环境,因此在脚本的第一行确定用于评估脚本主体的程序的解释器指令 (shebang) 不会找到它。patchShebangs在尼克斯商店里查找翻译,并修改了脚本 shebang。

<小时 />

0。介绍

在描述Nixpkgs标准环境的通用构建器的阶段时,Nixpkgs手册中间接提到了patchShebangs,指出修复阶段在某一点

将 shell 脚本的解释器路径重写为PATH中找到的路径。 例如,/usr/bin/perl将被重写为PATH中找到的/nix/store/some-perl/bin/perl。 ->

重要的是要注意(释义@jonringer的评论),">patchShebangs命令仅在构建期间可用,如果您获取$stdenv/setup安装钩子"(更多内容见下文)">stdenv(Nixpkgs标准环境)的默认构建器提供(使用stdenv.mkDerivation时默认得到这个),这就是为什么几乎所有nix表达式的起点都是import <nixpkgs> {}, stdenv.mkDerivation, 或类似的东西。">

1. 在哪里定义patchShebangs

Nixpkgs 存储库中的文件patch-shebangs.sh(也记录在 6.7.4. patch-shebangs.sh)定义了patchShebangs函数,该函数反过来用于实现patchShebangsAuto,即注册为在修复阶段运行的安装钩子。

2. 为什么在构建 Nix 包时需要重写 shebang

?根据patch-shebangs.sh顶部的评论:

# This setup hook causes the fixup phase to rewrite all script
# interpreter file names (`#!  /path') to paths found in $PATH.  E.g.,
# /bin/sh will be rewritten to /nix/store/<hash>-some-bash/bin/sh.
# /usr/bin/env gets special treatment so that ".../bin/env python" is
# rewritten to /nix/store/<hash>/bin/python.  Interpreters that are
# already in the store are left untouched.
# A script file must be marked as executable, otherwise it will not be
# considered.

重要说明:上面的标准是">脚本文件必须标记为可执行文件,否则将不被考虑"是一个重要的标准。

shell脚本中以#!开头的行称为 shebang(以及其他),它是执行 shell 的解释器指令,至于使用什么程序来破译下面的文本;#!后面的字符必须构成指向此可执行文件的绝对路径。例如,#!/usr/bin/python3希望在那里找到python3程序来执行用Python编程语言编写的shell脚本正文中的命令。

在包构建阶段使用 shell 脚本会变得有问题,因为

当 Nix 运行构建器时,它最初会完全清除环境(派生中声明的属性除外)。例如,PATH变量为空。这样做是为了防止在生成过程中使用未声明的输入。例如,如果 PATH 包含/usr/bin,则可能会意外使用/usr/bin/gcc。 ->

上面的引用来自 Nix 手册,但构建器(作为示例显示)使用$stdenv/setup- 一个 shell 脚本,为构建过程设置原始沙箱环境,从调用 shell 中取消设置大多数(所有?)环境变量,并且只包含少量实用程序。(这样做是为了使构建尽可能可重现。1

当将stdenv.mkDerivation与通用构建器一起使用时(即,当builder属性未声明时),通常隐式调用$stdenv/setup,但可以编写自己的构建器并在构建过程中显式调用它。

提示:这个答案显示了一种方法来查找某个 Nix 函数的定义位置(尽管它不是绝对可靠的)。

作为推论,shebang指令指出的程序不会在这些位置(或无法从沙盒访问),但它们实际上在Nix商店中(或将要)在Nix商店中,因此路径需要重新指向它们的位置。

注意:通用构建器从派生的输入填充 PATH,因此必须确保这些输入作为依赖项包含在内。

3.如何使用

3.1 隐式

如上所述,每当构建软件包时,patchShebangsAutosetup钩子都会在修复阶段自动调用patchShebangs- 除非通过设置dontPatchShebangs变量(或与此相关的dontFixup变量)来选择退出此操作(请参阅Nixpkgs手册中的控制修复阶段的变量)。

提醒自己:6.4 Bash 条件表达式。

3.1.0 自动调用时patchShebangs使用哪些脚本?

通常在软件包安装的脚本上(例如$out/bin)。

还是 Nixpkgs 标准库默认提供的那些?我认为这些必须足够通用才能在不同的平台上运行,以便 (1) 构建模板,以及 (2) 最终修补脚本 shebang。(@jtojnar证实了这个猜想,但本节需要参考,因此是小案例。

3.1.1 如何使用控制构建阶段的变量?

像任何其他控制构建器的变量一样将其传递给mkDerivation

stdenv.mkDerivation {
#...
dontPatchShebangs = true;
#...
}

3.2 明确

历史注释:最初,patchShebangs不可从外部调用,但后来被提取以使其功能在其他构建阶段中也可以重用。

同样,从实现中的注释来看:

# Run patch shebangs on a directory or file.
# Can take multiple paths as arguments.
# patchShebangs [--build | --host] PATH...
# Flags:
# --build : Lookup commands available at build-time
# --host  : Lookup commands available at runtime
# Example use cases,
# $ patchShebangs --host /nix/store/...-hello-1.0/bin
# $ patchShebangs --build configure

它需要在构建时直接执行的脚本(包括 shell 脚本)上运行。这些可能是

  1. 来自包装的来源
  2. 由一个人编写,在构建过程中用作帮助程序2

来自网络的具体示例:

  • 在 Nix 中,如何构建一个包含 Python 安装后脚本的软件包?(Unix & Linux Stackexchange)
  • 硬编码的 bin 路径和 NixOS(Stackoverflow)
  • [问题] NixOS 派生中的别名和符号链接 (Reddit)
  • 这个特定于 IRC
    的系统特定问题...并引用@jtojnar:

    这正是显式patchShebangs调用的用例。介子构建系统期望运行src/shared/generate-syscall-list.py所以它调用它。但这失败了,因为构建沙盒中不存在/usr/bin/env。它只会变得令人困惑,因为内核/libc/其他东西报告脚本不存在,即使它是不存在的 shebang 解释器。

>脚注[1]: TODO:详细了解沙盒是如何准确构建的,哪些是禁止的,哪些是允许的。引用@jtojnar举一个例子:

/usr/bin/env,这在沙盒中也不可用。(为了方便起见,NixOS仅在用户空间中具有它,但不会延续到Nix沙箱中。

[2]: @jtojnar的评论:">是的,对于仅在运行时执行的脚本,您不需要显式使用它,因为这些脚本将由隐式调用处理。">


此线程中的所有链接(希望)都已保存到互联网档案馆。(线程的配乐就是这个宝石。

最新更新