Различные методы для запуска исполняемого файла не-nixos на Nixos

12

Какие существуют методы для запуска исполняемого файла не nixos в NixO? Я хотел бы видеть также ручные методы.

tobiasBora
источник

Ответы:

22

Вот несколько методов (ручные в основном для образовательных целей, так как в большинстве случаев лучше написать правильный вывод). Я вообще не эксперт, и я сделал этот список также для изучения nix, так что если у вас есть лучшие методы, дайте мне знать!

Таким образом, основная проблема заключается в том, что исполняемый файл сначала вызывает загрузчик, а затем ему нужны некоторые библиотеки для работы, и nixos помещает как загрузчик, так и библиотеки /nix/store/.

В этом списке приведены все методы, которые я нашел до сих пор. Есть в основном три "группы":

  • полное руководство: интересно для образовательных целей и для понимания того, что происходит, но это все (не используйте их на практике, потому что ничто не помешает деривациям, использовавшимся позже для сбора мусора)
  • исправленные версии: эти методы пытаются изменить исполняемый файл (автоматически при использовании рекомендованного метода 4 с autoPatchelfHook), чтобы напрямую указывать на хорошую библиотеку
  • методы, основанные на FHS, которые в основном подделывают «нормальный linux» (более тяжелый для запуска, чем исправленная версия, поэтому по возможности этого следует избегать).

Я бы порекомендовал метод 4 с autoPatchelfHookдля реальной, правильной настройки, и если у вас нет времени и вы просто хотите запустить двоичный файл в одну строку, вас может заинтересовать быстрое и грязное решение, основанное на 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

Здесь загрузчик есть /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

Вам также нужно найти библиотеки, которые требуются вашей программе, например 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)

Здесь вы видите, что большинство библиотек найдено кроме libstdc++.so.6. Итак, давайте найдем это:

$ find /nix/store -name libstdc++.so.6
/nix/store/12zhmzzhrwszdc8q3fwgifpwjkwi3mzc-gcc-7.3.0-lib/lib/libstdc++.so.6

Хорошо. Теперь нам просто нужно запустить программу с 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

(Обязательно используйте ./перед именем сценария и сохраняйте только каталог библиотек. Если у вас несколько библиотек, просто используйте путь concat с двоеточиями)

Метод 2) Грязный ручной метод, с патчем

После установки (с nixenv -iили в ваш configuration.nix) patchelfвы также можете напрямую изменить исполняемый файл, чтобы упаковать хороший загрузчик и библиотеки. Чтобы изменить загрузчик, просто запустите:

patchelf --set-interpreter /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2 wolframscript

и проверить:

$ patchelf --print-interpreter wolframscript
/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.

и чтобы изменить путь к библиотекам, жестко закодированным в исполняемом файле, сначала проверьте текущий rpath (пустой для меня):

$ patchelf --print-rpath wolframscript

и добавьте их к указанному ранее пути к библиотеке, в конце концов разделив двоеточиями:

$ patchelf --set-rpath /nix/store/12zhmzzhrwszdc8q3fwgifpwjkwi3mzc-gcc-7.3.0-lib/lib/ wolframscript
$ ./wolframscript

Способ 3) Патч в выводе NIX

Мы можем воспроизвести более или менее одно и то же в выводе nix, вдохновленном skypeforlinux

Этот пример представляет также альтернативу, которую вы можете использовать:

patchelf --set-interpreter ${glibc}/lib/ld-linux-x86-64.so.2 "$out/bin/wolframscript" || true

(что должно быть достаточно ясно, как только вы поймете «ручной» метод), или

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

Этот второй метод немного более тонкий, но если вы запустите:

$ 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

вы увидите, что файл $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" ];
  };
}

и на default.nixместе:

{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}

Скомпилируйте и запустите

nix-build
result/bin/wolframscript

Способ 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" ];
  };
}

Способ 5) Используйте FHS для имитации классической оболочки Linux и вручную запустите файлы

Некоторое программное обеспечение может быть сложно упаковать таким образом, поскольку оно может сильно зависеть от структуры файлового дерева FHS или может проверить, что двоичный файл не изменился. Затем вы также можете использовать buildFHSUserEnv для предоставления файловой структуры FHS (облегченной, с использованием пространств имен) для вашего приложения. Обратите внимание, что этот метод тяжелее, чем методы на основе исправлений, и добавляет значительное время запуска, поэтому избегайте его, когда это возможно

Вы можете либо просто создать оболочку, а затем вручную извлечь архив и выполнить файл, либо напрямую упаковать свою программу для FHS. Давайте сначала посмотрим, как получить оболочку. Поместите в файл (скажем fhs-env.nix) следующее:

let nixpkgs = import <nixpkgs> {};
in nixpkgs.buildFHSUserEnv {
   name = "fhs";
   targetPkgs = pkgs: [];
   multiPkgs = pkgs: [ pkgs.dpkg ];
   runScript = "bash";
}

и запустить:

nix-build fhs-env.nix
result/bin/fhs

Затем вы получите bash в более стандартно выглядящем linux и можете запускать команды для запуска вашего исполняемого файла, например:

mkdir wolf_fhs/
dpkg -x WolframScript_12.0.0_LINUX64_amd64.deb wolf_fhs/
cd wolf_fhs/opt/Wolfram/WolframScript/bin/
./wolfram

Если вам нужно больше библиотек / программ в качестве зависимостей, просто добавьте их в multiPkgs(для всех поддерживаемых арок) или targetPkgs(только для текущей арки).

Бонус: вы также можете запустить оболочку fhs с помощью однострочной команды, не создавая специальный файл:

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

Метод 6) Используйте FHS для имитации классической оболочки Linux и упакуйте файлы в

источник: https://reflexivereflection.com/posts/2015-02-28-deb-installation-nixos.html

Способ 7) паровой прогон

С buildFHSUserEnvего помощью можно запускать множество программ, но вам нужно будет вручную указать все необходимые библиотеки. Если вам нужно быстрое решение, и у вас нет времени, чтобы точно проверить, какие библиотеки вам нужны, вы можете попробовать steam-run(несмотря на название, оно не связано напрямую с Steam, а просто упаковывает много библиотек), что как buildFHSUserEnvи во многих предустановленных общих библиотеках (некоторые из них могут быть несвободными, например steamrt, упаковывает некоторый код nvidia, спасибо Симпсон!). Чтобы использовать его, просто установите steam-run, а затем:

steam-run ./wolframscript

или если вы хотите полную оболочку:

steam-run bash

Обратите внимание , что вам может понадобиться , чтобы добавить 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 "$@"

или прямо напишите это в выводе nixos:

{ stdenv, steam-run, writeScriptBin }:
let
  src = ./opt/Wolfram/WolframScript/bin/wolframscript;
in writeScriptBin "wolf_wrapped_steam" ''
    exec ${steam-run}/bin/steam-run ${src} "$@"
  ''

или если вы начинаете с .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
  '';
}

(если вы слишком устали, чтобы писать обычные default.nix, вы можете запустить напрямую nix-build -E "with import <nixpkgs> {}; callPackage ./derivation.nix {}")

Способ 8) Использование контейнеров / Docker (гораздо тяжелее)

ДЕЛАТЬ

Метод 9) Положитесь на плоский пакет / appimage

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

appimage-run: для тестирования, например, musescore

Источники или примеры

tobiasBora
источник