Diferentes métodos para ejecutar un ejecutable que no sea nixos en Nixos

12

¿Cuáles son los diferentes métodos para ejecutar un ejecutable que no sea nixos en NixOs? Me gustaría ver también los métodos manuales.

tobiasBora
fuente

Respuestas:

22

Aquí hay varios métodos (los manuales son principalmente para fines educativos, ya que la mayoría de las veces es mejor escribir una derivación adecuada). No soy un experto en absoluto, e hice esta lista también para aprender nix, así que si tienes mejores métodos, ¡házmelo saber!

Entonces, el problema principal es que el ejecutable llama primero a un cargador, y luego necesita algunas bibliotecas para funcionar, y nixos coloca tanto el cargador como las bibliotecas /nix/store/.

Esta lista ofrece todos los métodos que encontré hasta ahora. Básicamente hay tres "grupos":

  • el manual completo: interesante para fines educativos y para comprender lo que está sucediendo, pero eso es todo (no los use en la práctica porque nada evitará que las derivaciones que solían recogerse más tarde)
  • las versiones parcheadas: estos métodos intentan modificar el ejecutable (automáticamente cuando se utiliza el método recomendado 4 con autoPatchelfHook) para señalar directamente la buena biblioteca
  • los métodos basados ​​en FHS, que básicamente simulan un "linux normal" (más pesado de ejecutar que la versión parcheada, por lo que esto debería evitarse si es posible).

Recomendaría el método 4 autoPatchelfHookpara una configuración real y adecuada, y si no tiene tiempo y solo desea ejecutar un binario en una línea, puede interesarle la solución rápida y sucia basada en steam-run(método 7 )

Método 1) Método manual sucio, sin parche

Primero debe encontrar el cargador con, por ejemplo 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

Aquí está el cargador /lib64/ld-linux-x86-64.so.2. Para encontrar el cargador de nixos, puedes hacer:

$ ls /nix/store/*glibc*/lib/ld-linux-x86-64.so.2
/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2

También debe buscar para encontrar las bibliotecas que requiere su programa, por ejemplo con 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)

Aquí, verá que la mayoría de las bibliotecas se encuentran excepto libstdc++.so.6. Así que vamos a encontrarlo:

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

Bueno. Ahora, solo necesitamos ejecutar el programa con el LD_LIBRARY_PATHconfigurado para apuntar a este archivo, y llamar al cargador que determinamos en el primer paso en este archivo:

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

(asegúrese de usar ./antes del nombre del script y de mantener solo el directorio de las bibliotecas. Si tiene varias bibliotecas, simplemente use concat la ruta con dos puntos)

Método 2) Método manual sucio, con parche

Después de la instalación (con nixenv -io en su configuration.nix) patchelf, también puede modificar directamente el ejecutable para empacar el buen cargador y las bibliotecas. Para cambiar el cargador simplemente ejecute:

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

y para comprobar:

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

y para cambiar la ruta a las bibliotecas codificadas en el ejecutable, primero verifique cuál es la ruta actual (vacía para mí):

$ patchelf --print-rpath wolframscript

y añádalos a la ruta de la biblioteca que determinó antes, finalmente separados con dos puntos:

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

Método 3) Parche en una derivación de nix

Podemos reproducir más o menos lo mismo en una derivación de nix inspirada en skypeforlinux

Este ejemplo también presenta una alternativa, puede usar:

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

(que debería quedar bastante claro una vez que comprenda el método "manual"), o

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

Este segundo método es un poco más sutil, pero si ejecuta:

$ 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

verá que el archivo $NIX_CC/nix-support/dynamic-linkercontiene una ruta al cargador ld-linux-x86-64.so.2.

Ponlo derivation.nix, esto es

{ 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" ];
  };
}

y en el default.nixpuesto:

{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}

Compilar y ejecutar con

nix-build
result/bin/wolframscript

Método 4) Use autoPatchElf: más simple

Todos los métodos anteriores necesitan un poco de trabajo (necesita encontrar los ejecutables, parchearlos ...). ¡NixOs hizo por nosotros un "gancho" especial autoPatchelfHookque automáticamente repara todo por ti! Solo necesita especificarlo (native)BuildInputs, y nix hace la magia.

{ 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" ];
  };
}

Método 5) Use FHS para simular un shell de Linux clásico y ejecute manualmente los archivos

Algunos sofware pueden ser difíciles de empaquetar de esa manera porque pueden depender en gran medida de la estructura de árbol de archivos FHS , o pueden verificar que los binarios no hayan cambiado. Luego, también puede usar buildFHSUserEnv para proporcionar una estructura de archivos FHS (ligera, usando espacios de nombres) para su aplicación. Tenga en cuenta que este método es más pesado que los métodos basados ​​en parches y agrega un tiempo de inicio significativo, así que evítelo cuando sea posible

Puede generar un shell y luego extraer manualmente el archivo y ejecutar el archivo, o directamente empaquetar su programa para el FHS. Veamos primero cómo obtener un caparazón. Ponga en un archivo (digamos fhs-env.nix) lo siguiente:

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

y correr:

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

Luego obtendrá un bash en un linux de aspecto más estándar, y puede ejecutar comandos para ejecutar su ejecutable, como:

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

Si necesita más bibliotecas / programas como dependencias, simplemente agréguelos a multiPkgs(para todos los archivos compatibles) o targetPkgs(solo para el archivo actual).

Bonificación: también puede iniciar un shell fhs con un comando de una línea, sin crear un archivo específico:

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

Método 6) Use FHS para simular un shell clásico de Linux y empaque los archivos dentro

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

Método 7) ejecución de vapor

Con buildFHSUserEnvusted puede ejecutar muchos softwares, pero deberá especificar manualmente todas las bibliotecas necesarias. Si desea una solución rápida y no tiene tiempo para verificar con precisión cuáles son las bibliotecas requeridas, puede intentarlo steam-run(a pesar del nombre, no está vinculado directamente con steam, y solo contiene muchas bibliotecas), que es al igual que buildFHSUserEnvcon muchas bibliotecas comunes preinstaladas (algunas de ellas pueden no ser libres, como steamrteso contiene algún código de nvidia, ¡gracias, simpson!). Para usarlo, simplemente instale steam-runy luego:

steam-run ./wolframscript

o si quieres un caparazón completo:

steam-run bash

Tenga en cuenta que puede ser necesario añadir nixpkgs.config.allowUnfree = true;(o lista blanca de este paquete específico ) si desea instalarlo con nixos-rebuild, y si se desea ejecutar / instalarlo con nix-shell/ nix-envque necesita para poner { allowUnfree = true; }en ~/.config/nixpkgs/config.nix.

No es fácil "sobrescribir" paquetes o bibliotecas a nix-shell, pero si desea crear un contenedor alrededor de su script, puede crear manualmente un script de contenedor:

#!/usr/bin/env nix-shell
#!nix-shell -i bash -p steam-run
exec steam-run ./wolframscript "$@"

o escribirlo directamente en una derivación de nixos:

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

o si comienzas desde el .deb (aquí usé en su makeWrapperlugar):

{ 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
  '';
}

(si estás demasiado cansado para escribir lo habitual default.nix, puedes correr directamente nix-build -E "with import <nixpkgs> {}; callPackage ./derivation.nix {}")

Método 8) Usar contenedores / Docker (mucho más pesado)

QUE HACER

Método 9) Confíe en flatpack / appimage

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

appimage-run: Para probar con, por ejemplo, musescore

Fuentes o ejemplos

tobiasBora
fuente