在 Nixos 上运行非 nixos 可执行文件的不同方法

tob*_*ora 29 executable nixos

在 NixOs 上运行非 nixos 可执行文件有哪些不同的方法?我也想看看手动方法。

tob*_*ora 51

这里有几种方法(手动方法主要用于教育目的,因为大多数情况下编写正确的推导会更好)。我根本不是专家,我做这个列表也是为了学习 nix,所以如果你有更好的方法,请告诉我!

所以主要问题是可执行文件首先调用加载器,然后需要一些库才能工作,而 nixos 将加载器和库都放在/nix/store/.

这个列表给出了我目前找到的所有方法。基本上有三个“组”:

  • 完整手册:对于教育目的很有趣,并了解正在发生的事情,但这就是全部(不要在实践中使用它们,因为没有什么可以阻止用于以后垃圾收集的派生)
  • 修补版本:这些方法尝试修改可执行文件(在使用推荐的方法 4 和 autoPatchelfHook 时自动修改)以直接指向好的库
  • 基于 FHS 的方法,基本上是假装一个“普通的 linux”(比打补丁的版本更重,所以如果可能的话应该避免这种情况)。

我建议使用方法 4autoPatchelfHook进行真实、正确的设置,如果您没有时间而只想在一行中运行二进制文件,您可能会对基于steam-run(方法 7 )。

方法1)脏手动方法,没有补丁

您需要首先找到加载程序,例如file

$ file wolframscript
wolframscript: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=079684175aa38e3633b60544681b338c0e8831e0, stripped
Run Code Online (Sandbox Code Playgroud)

装载机在这里/lib64/ld-linux-x86-64.so.2。要找到 nixos 的加载程序,您可以执行以下操作:

$ ls /nix/store/*glibc*/lib/ld-linux-x86-64.so.2
/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
Run Code Online (Sandbox Code Playgroud)

你还需要找到你的程序需要的库,例如ldd

$ ldd wolframscript
        linux-vdso.so.1 (0x00007ffe8fff9000)
        libpthread.so.0 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/libpthread.so.0 (0x00007f86aa321000)
        librt.so.1 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/librt.so.1 (0x00007f86aa317000)
        libdl.so.2 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/libdl.so.2 (0x00007f86aa312000)
        libstdc++.so.6 => not found
        libm.so.6 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/libm.so.6 (0x00007f86aa17c000)
        libgcc_s.so.1 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/libgcc_s.so.1 (0x00007f86a9f66000)
        libc.so.6 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/libc.so.6 (0x00007f86a9dae000)
        /lib64/ld-linux-x86-64.so.2 => /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib64/ld-linux-x86-64.so.2 (0x00007f86aa344000)
Run Code Online (Sandbox Code Playgroud)

在这里,您会看到除了libstdc++.so.6. 所以让我们找到它:

$ find /nix/store -name libstdc++.so.6
/nix/store/12zhmzzhrwszdc8q3fwgifpwjkwi3mzc-gcc-7.3.0-lib/lib/libstdc++.so.6
Run Code Online (Sandbox Code Playgroud)

好的。现在,我们只需要运行LD_LIBRARY_PATH配置为指向此文件的程序,并调用我们在此文件的第一步中确定的加载程序:

LD_LIBRARY_PATH=/nix/store/12zhmzzhrwszdc8q3fwgifpwjkwi3mzc-gcc-7.3.0-lib/lib/:$LD_LIBRARY_PATH /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2 ./wolframscript
Run Code Online (Sandbox Code Playgroud)

(确保./在脚本名称之前使用,并且只保留库的目录。如果您有多个库,只需使用冒号连接路径)

方法2)脏手动方法,带补丁

安装后(使用nixenv -i或在您的configuration.nixpatchelf,您还可以直接修改可执行文件以打包好的加载程序和库。要更改加载程序,只需运行:

patchelf --set-interpreter /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2 wolframscript
Run Code Online (Sandbox Code Playgroud)

并检查:

$ patchelf --print-interpreter wolframscript
/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.
Run Code Online (Sandbox Code Playgroud)

并更改可执行文件中硬编码的库的路径,首先检查当前 rpath 是什么(对我来说是空的):

$ patchelf --print-rpath wolframscript
Run Code Online (Sandbox Code Playgroud)

并将它们附加到您之前确定的库路径中,最终用冒号分隔:

$ patchelf --set-rpath /nix/store/12zhmzzhrwszdc8q3fwgifpwjkwi3mzc-gcc-7.3.0-lib/lib/ wolframscript
$ ./wolframscript
Run Code Online (Sandbox Code Playgroud)

方法 3) 在 nix 派生中打补丁

我们可以在受skypeforlinux启发的 nix 派生中或多或少地重现相同的东西

此示例还提供了一种替代方法,您可以使用:

patchelf --set-interpreter ${glibc}/lib/ld-linux-x86-64.so.2 "$out/bin/wolframscript" || true
Run Code Online (Sandbox Code Playgroud)

(一旦你理解了“手动”方法,这应该很清楚),或者

patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$out/bin/wolframscript" || true
Run Code Online (Sandbox Code Playgroud)

第二种方法有点微妙,但如果你运行:

$ nix-shell '<nixpkgs>' -A hello --run 'echo $NIX_CC/nix-support/dynamic-linker "->" $(cat $NIX_CC/nix-support/dynamic-linker)'
/nix/store/8zfm4i1aw4c3l5n6ay311ds6l8vd9983-gcc-wrapper-7.4.0/nix-support/dynamic-linker -> /nix/store/sw54ph775lw7b9g4hlfvpx6fmlvdy8qi-glibc-2.27/lib/ld-linux-x86-64.so.2
Run Code Online (Sandbox Code Playgroud)

您将看到该文件$NIX_CC/nix-support/dynamic-linker包含加载程序的路径ld-linux-x86-64.so.2

放入derivation.nix,这是

{ stdenv, dpkg,glibc, gcc-unwrapped }:
let

  # Please keep the version x.y.0.z and do not update to x.y.76.z because the
  # source of the latter disappears much faster.
  version = "12.0.0";

  rpath = stdenv.lib.makeLibraryPath [
    gcc-unwrapped
    glibc
  ];
  # What is it for?
  # + ":${stdenv.cc.cc.lib}/lib64";

  src = ./WolframScript_12.0.0_LINUX64_amd64.deb;

in stdenv.mkDerivation {
  name = "wolframscript-${version}";

  system = "x86_64-linux";

  inherit src;

  nativeBuildInputs = [
  ];

  buildInputs = [ dpkg ];

  unpackPhase = "true";

  # Extract and copy executable in $out/bin
  installPhase = ''
    mkdir -p $out
    dpkg -x $src $out
    cp -av $out/opt/Wolfram/WolframScript/* $out
    rm -rf $out/opt
  '';

  postFixup = ''
    # Why does the following works?
    patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$out/bin/wolframscript" || true
    # or
    # patchelf --set-interpreter ${glibc}/lib/ld-linux-x86-64.so.2 "$out/bin/wolframscript" || true
    patchelf --set-rpath ${rpath} "$out/bin/wolframscript" || true
  '';

  meta = with stdenv.lib; {
    description = "Wolframscript";
    homepage = https://www.wolfram.com/wolframscript/;
    license = licenses.unfree;
    maintainers = with stdenv.lib.maintainers; [ ];
    platforms = [ "x86_64-linux" ];
  };
}

Run Code Online (Sandbox Code Playgroud)

并输入default.nix

{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}
Run Code Online (Sandbox Code Playgroud)

编译并运行

nix-build
result/bin/wolframscript
Run Code Online (Sandbox Code Playgroud)

方法4)使用autoPatchElf:更简单

所有以前的方法都需要一些工作(您需要找到可执行文件,修补它们......)。NixOs 为我们做了一个特殊的“钩子” autoPatchelfHook,它会自动为你修补一切!您只需要在 中指定它(native)BuildInputs,nix就可以发挥作用。

{ stdenv, dpkg, glibc, gcc-unwrapped, autoPatchelfHook }:
let

  # Please keep the version x.y.0.z and do not update to x.y.76.z because the
  # source of the latter disappears much faster.
  version = "12.0.0";

  src = ./WolframScript_12.0.0_LINUX64_amd64.deb;

in stdenv.mkDerivation {
  name = "wolframscript-${version}";

  system = "x86_64-linux";

  inherit src;

  # Required for compilation
  nativeBuildInputs = [
    autoPatchelfHook # Automatically setup the loader, and do the magic
    dpkg
  ];

  # Required at running time
  buildInputs = [
    glibc
    gcc-unwrapped
  ];

  unpackPhase = "true";

  # Extract and copy executable in $out/bin
  installPhase = ''
    mkdir -p $out
    dpkg -x $src $out
    cp -av $out/opt/Wolfram/WolframScript/* $out
    rm -rf $out/opt
  '';

  meta = with stdenv.lib; {
    description = "Wolframscript";
    homepage = https://www.wolfram.com/wolframscript/;
    license = licenses.mit;
    maintainers = with stdenv.lib.maintainers; [ ];
    platforms = [ "x86_64-linux" ];
  };
}
Run Code Online (Sandbox Code Playgroud)

方法5)使用FHS模拟经典的linux shell,手动执行文件

某些软件可能很难以这种方式打包,因为它们可能严重依赖FHS文件树结构,或者可能会检查二进制文件是否未更改。然后,您还可以使用buildFHSUserEnv为您的应用程序提供 FHS 文件结构(轻量级,使用命名空间)。请注意,此方法比基于补丁的方法更重,并且会增加大量启动时间,因此请尽可能避免使用

您可以只生成一个 shell,然后手动提取存档并执行文件,或者直接为 FHS 打包您的程序。我们先来看看如何获​​得一个shell。将fhs-env.nix以下内容放入文件中(例如):

let nixpkgs = import <nixpkgs> {};
in nixpkgs.buildFHSUserEnv {
   name = "fhs";
   targetPkgs = pkgs: [];
   multiPkgs = pkgs: [ pkgs.dpkg ];
   runScript = "bash";
}
Run Code Online (Sandbox Code Playgroud)

并运行:

nix-build fhs-env.nix
result/bin/fhs
Run Code Online (Sandbox Code Playgroud)

然后,您将在看起来更标准的 linux 中获得 bash,并且您可以运行命令来运行您的可执行文件,例如:

mkdir wolf_fhs/
dpkg -x WolframScript_12.0.0_LINUX64_amd64.deb wolf_fhs/
cd wolf_fhs/opt/Wolfram/WolframScript/bin/
./wolfram
Run Code Online (Sandbox Code Playgroud)

如果您需要更多库/程序作为依赖项,只需将它们添加到multiPkgs(对于所有支持的targetPkgsarch )或(仅对于当前 arch)。

奖励:您还可以使用单行命令启动 fhs shell,而无需创建特定文件:

nix-build -E '(import <nixpkgs> {}).buildFHSUserEnv {name = "fhs";}' && ./result/bin/fhs
Run Code Online (Sandbox Code Playgroud)

方法6)使用FHS模拟一个经典的linux shell,将里面的文件打包

来源:https : //reflexivereflection.com/posts/2015-02-28-deb-installation-nixos.html

方法 7) 蒸汽运行

随着buildFHSUserEnv您可以运行软件的很多的,但是你需要手动指定所有需要的库。如果您想要一个快速的解决方案并且您没有时间精确检查所需的库是什么,您可能想尝试steam-run(尽管名称,它没有直接与 Steam 链接,只是打包了很多库),这是就像buildFHSUserEnv预装了许多常用库一样(其中一些可能是非免费的,就像steamrt打包了一些 nvidia 代码一样,谢谢辛普森!)。要使用它,只需安装steam-run,然后:

steam-run ./wolframscript
Run Code Online (Sandbox Code Playgroud)

或者如果你想要一个完整的外壳:

steam-run bash
Run Code Online (Sandbox Code Playgroud)

请注意,您可能需要添加nixpkgs.config.allowUnfree = true;(或白名单这个特定的包),如果你想与安装它nixos-rebuild,如果你想运行与/安装nix-shell/nix-env你需要把{ allowUnfree = true; }~/.config/nixpkgs/config.nix

将包或库“覆盖”到 nix-shell 并不容易,但是如果您想对脚本进行包装,您可以手动创建包装脚本:

#!/usr/bin/env nix-shell
#!nix-shell -i bash -p steam-run
exec steam-run ./wolframscript "$@"
Run Code Online (Sandbox Code Playgroud)

或直接写在 nixos 派生中:

{ stdenv, steam-run, writeScriptBin }:
let
  src = ./opt/Wolfram/WolframScript/bin/wolframscript;
in writeScriptBin "wolf_wrapped_steam" ''
    exec ${steam-run}/bin/steam-run ${src} "$@"
  ''
Run Code Online (Sandbox Code Playgroud)

或者如果你从 .deb 开始(我在这里使用makeWrapper):

{ stdenv, steam-run, dpkg, writeScriptBin, makeWrapper }:
stdenv.mkDerivation {
  name = "wolframscript";
  src = ./WolframScript_12.0.0_LINUX64_amd64.deb;

  nativeBuildInputs = [
    dpkg makeWrapper
  ];
  unpackPhase = "true";
  installPhase = ''
    mkdir -p $out/bin
    dpkg -x $src $out
    cp -av $out/opt/Wolfram/WolframScript/bin/wolframscript $out/bin/.wolframscript-unwrapped
    makeWrapper ${steam-run}/bin/steam-run $out/bin/wolframscript --add-flags $out/bin/.wolframscript-unwrapped
    rm -rf $out/opt
  '';
}

Run Code Online (Sandbox Code Playgroud)

(如果平时写的太累default.nix,可以直接运行nix-build -E "with import <nixpkgs> {}; callPackage ./derivation.nix {}"

方法 8)使用容器/Docker(更重)

去做

方法 9) 依赖 flatpack/appimage

https://nixos.org/nixos/manual/index.html#module-services-flatpak

appimage-run :使用例如 musescore 进行测试

来源或例子

  • 引用玛丽亚·凯莉的话说,“[那真是……太棒了](https://youtu.be/O4ResuAjG8k?t=176)”。 (5认同)
  • tl;dr: `nix-shell -p steam-run --run "steam-run ./the-binary"` (5认同)