在查看 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 隐式
如上所述,每当构建软件包时,patchShebangsAuto
setup钩子都会在修复阶段自动调用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 脚本)上运行。这些可能是
- 来自包装的来源
- 由一个人编写,在构建过程中用作帮助程序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的评论:">是的,对于仅在运行时执行的脚本,您不需要显式使用它,因为这些脚本将由隐式调用处理。">
此线程中的所有链接(希望)都已保存到互联网档案馆。(线程的配乐就是这个宝石。