如何诊断失败的 nix 构建?

如何诊断失败的 nix 构建?

如何诊断 nix 构建失败?

目前我看到的输出为:

nix build -v
warning: dumping very large path (> 256 MiB); this may run out of memory
building '/nix/store/fdrm6kbm68vld3bhfjizv684ck725lyf-blog.drv'...
builder for '/nix/store/fdrm6kbm68vld3bhfjizv684ck725lyf-blog.drv' failed with exit code 1; last 5 log lines:
  unpacking sources
  unpacking source archive /nix/store/s7r5vlvp49ad6a9d5hqhsiaxw691iyhf-Blog
  source root is Blog
  patching sources
  configuring
[0 built (1 failed), 0.0 MiB DL]
error: build of '/nix/store/fdrm6kbm68vld3bhfjizv684ck725lyf-blog.drv' failed

我希望找到一些日志/错误来说明为什么它无法构建?


继自https://stackoverflow.com/a/47264375/1663462:

我尝试添加build-cache-failures = true;default.nix但我仍然看不到输出:

nix-store --read-log

结果nix-store --query-failed-paths是:

error: no operation specified
Try 'nix-store --help' for more information.

答案1

也可以看看:

调试包的构建过程:

代替nix-build

nix-build -E 'with import <nixpkgs> {}; callPackage ./. {}'

运行nix-shell进入构建环境

nix-shell -E 'with import <nixpkgs> {}; callPackage ./. {}'

然后运行构建阶段

# run build in tempdir
cd $(mktemp -d)

# dont install to /nix/store
for n in $outputs; do eval export $n=$PWD/result-$n; done

# https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh
phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \
    configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \
    ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \
    ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}";

echo writing nix-build-phases.txt
for curPhase in $phases; do
    echo "eval \"\${$curPhase:-$curPhase}\""
    if [ "$curPhase" = unpackPhase ]; then
        echo '[ -n "${sourceRoot:-}" ] && chmod +x "${sourceRoot}"'
        echo 'cd "${sourceRoot:-.}"'
    fi
done >nix-build-phases.txt

echo "please manually run the build phases in nix-build-phases.txt"
echo
cat nix-build-phases.txt

示例 nix-build-phases.txt

eval "${unpackPhase:-unpackPhase}"
[ -n "${sourceRoot:-}" ] && chmod +x "${sourceRoot}"
cd "${sourceRoot:-.}"
eval "${patchPhase:-patchPhase}"
eval "${updateAutotoolsGnuConfigScriptsPhase:-updateAutotoolsGnuConfigScriptsPhase}"
eval "${configurePhase:-configurePhase}"
eval "${buildPhase:-buildPhase}"
eval "${checkPhase:-checkPhase}"
eval "${installPhase:-installPhase}"
eval "${fixupPhase:-fixupPhase}"
eval "${installCheckPhase:-installCheckPhase}"
eval "${distPhase:-distPhase}"

在此构建环境中,您可以

  • 找出构建错误的原因(哪个构建阶段失败了?)
  • 修改构建命令,例如重新定义buildPhase:运行type buildPhase打印旧阶段、复制旧阶段、修改阶段、粘贴新阶段
  • 运行修改后的构建命令。这可能比完全重建更快

注意:构建者可以执行不同的构建阶段。实际的构建阶段存储在变量中,例如prePhasespreConfigurePhases

问题:构建脚本运行,set -e因此它将在出现第一个错误时退出,但这会退出交互式 shell。这是 bash shell 的限制,因此理想情况下,我们将在 bash 调试器中运行构建脚本,在出现第一个错误时停止执行,修改命令,后退并重复执行命令,并允许检查状态。

# run build in tempdir
cd $(mktemp -d)

# dont install to /nix/store
for n in $outputs; do eval export $n=$PWD/result-$n; done

# https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh
phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \
    configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \
    ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \
    ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}";

# test
#patchPhase="echo test error in patchPhase; false"

donePhases=()
missingPhases=($phases)
for curPhase in $phases; do
    # no. genericBuild does not "return 1" on error
    #phases="$curPhase" genericBuild || break
    echo
    echo "# running $curPhase"
    # FIXME this also traps suspend (Ctrl-Z)
    trap '
        echo "FIXME error in $curPhase"
        echo "done phases:" ${donePhases[@]}
        echo "missing phases:" ${missingPhases[@]}
        echo "hit Enter to close the nix-shell"
        read
    ' EXIT
    set -e # exit on error
    set -x # trace
    eval "${!curPhase:-$curPhase}"
    set +x
    set +e
    if [ "$curPhase" = unpackPhase ]; then
        [ -n "${sourceRoot:-}" ] && chmod +x "${sourceRoot}"
        cd "${sourceRoot:-.}"
    fi
    donePhases+=($curPhase)
    missingPhases=(${missingPhases[@]:1})
done

答案2

阅读日志应该有效。你有具体说明推导过程吗? nix-store --read-log /nix/store/fdrm6kbm68vld3bhfjizv684ck725lyf-blog.drv

答案3

有一个--debug选项以及一个可以提供附加信息的--print-build-logs选项(打开nix build但不是)。nix-build

关于具体问题 - 可能是这个错误:https://github.com/NixOS/nix/issues/2176

更多信息

https://releases.nixos.org/nix/nix-0.16/build/565033/download/1/manual/index.html#id494024

答案4

添加失败的breakpointHook推导nativeBuildInputs也是一种强大的技术——它可以让你与cntr并探索派生构建失败时的环境。

感谢 jamesbrock 在 NixOS Discourse 上的发言,它的缺点是:

将包添加breakpointHooknativeBuildInputsa 的mkDerivation.

nativeBuildInputs = [ breakpointHook ];

安装中心

nix-env -i cntr

运行 Nix 构建命令,当它失败时,breakpointHook 将输出如下消息:

build failed in installPhase with exit code 1
To attach install cntr and run the following command as root:

   cntr attach -t command cntr-/nix/store/6vwxqrwq5h1fd3nw4mc61wgk7rppn2qw-jupyterlab-extended

所以我们运行该命令。 root 用户没有我们在 Nix 配置文件中安装的PATH命令cntr,因此请为其提供单用户安装的 cntr 的完整路径。

sudo /home/$USER/.nix-profile/bin/cntr attach -t command cntr-/nix/store/6vwxqrwq5h1fd3nw4mc61wgk7rppn2qw-jupyterlab-extended

现在我们在沉睡的尼克斯容器中。请注意,像和
这样的路径在此阶段与您常用的工具一起可用,但是它们位于当前目录 ( ) 下,而不是. (您可以像这样使用它们。)$out$TMPDIR/var/lib/cntr/
cd $(echo $out | cut -c2-)

从这里,运行cntr exec以完全加载构建环境。

/home/$USER/.nix-profile/bin/cntr exec

$TMPDIR目录是 Nix 为构建 10 创建临时目录的位置。

cd $TMPDIR

buildPhase现在我们完全处于构建的上下文中,并且我们在 shell 中输入的任何命令都将如同我们在派生失败时在派生阶段(例如 )的一行中输入该命令一样。


提及在 NixOS 讨论中:

我还想添加一些有关 BreakpointHook 的细节。这是一个非常强大的调试钩子@Mic92

将 pkgs.breakpointHook 放入 buildInputs 中即可使其正常工作。如果出现故障,则会在控制台上打印出准确的“连接”命令。您可以使用此命令 (cntr) 将 shell 附加到构建环境中。要使用它,您必须能够与 cntr 一起运行 sudo (因此您应该在之前安装 cntr 包;-))。

您可以在 @Mic92 NixCon 演示文稿中找到更多有趣的细节:Jörg 'Mic92' Thalheim - 关于 Nix 沙箱和断点 (NixCon 2018)

这是手册相关部分的链接:https://nixos.org/manual/nixpkgs/stable/#breakpointhook

相关内容