Différentes méthodes pour exécuter un exécutable non nixos sur Nixos

12

Quelles sont les différentes méthodes pour exécuter un exécutable non nixos sur NixOs? J'aimerais voir aussi les méthodes manuelles.

tobiasBora
la source

Réponses:

22

Voici plusieurs méthodes (les méthodes manuelles sont principalement à des fins éducatives car la plupart du temps, il est préférable d'écrire une dérivation appropriée). Je ne suis pas du tout un expert, et j'ai également fait cette liste pour apprendre Nix, donc si vous avez de meilleures méthodes, faites le moi savoir!

Donc, le problème principal est que l'exécutable appelle d'abord un chargeur, puis a besoin de certaines bibliothèques pour fonctionner, et nixos place à la fois le chargeur et les bibliothèques /nix/store/.

Cette liste donne toutes les méthodes que j'ai trouvées jusqu'à présent. Il existe essentiellement trois "groupes":

  • le manuel complet: intéressant à des fins pédagogiques et pour comprendre ce qui se passe, mais c'est tout (ne les utilisez pas dans la pratique car rien n'empêchera les dérivations d'être récupérées plus tard)
  • les versions corrigées: ces méthodes tentent de modifier l'exécutable (automatiquement lors de l'utilisation de la méthode recommandée 4 avec autoPatchelfHook) pour indiquer directement la bonne bibliothèque
  • les méthodes basées sur FHS, qui simulent fondamentalement un "linux normal" (plus lourd à exécuter que la version corrigée, donc cela devrait être évité si possible).

Je recommanderais la méthode 4 avec autoPatchelfHookpour une configuration réelle et correcte, et si vous n'avez pas le temps et que vous souhaitez simplement exécuter un binaire en une seule ligne, vous pouvez être intéressé par la solution rapide et sale basée sur steam-run(méthode 7 ).

Méthode 1) Méthode manuelle sale, pas de patch

Vous devez d'abord trouver le chargeur avec par exemple 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

Voici le chargeur /lib64/ld-linux-x86-64.so.2. Pour trouver le chargeur de nixos, vous pouvez faire:

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

Vous devez également rechercher pour trouver les bibliothèques dont votre programme a besoin, par exemple avec 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)

Ici, vous voyez que la plupart des bibliothèques se trouvent sauf libstdc++.so.6. Alors trouvons-le:

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

Bien. Maintenant, nous devons simplement exécuter le programme avec le LD_LIBRARY_PATHconfiguré pour pointer vers ce fichier et appeler le chargeur que nous avons déterminé à la première étape de ce fichier:

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

(assurez-vous d'utiliser ./avant le nom du script et de ne conserver que le répertoire des bibliothèques. Si vous avez plusieurs bibliothèques, utilisez simplement concat le chemin avec deux points)

Méthode 2) Méthode manuelle sale, avec patch

Après l'installation (avec nixenv -iou dans votre configuration.nix) patchelf, vous pouvez également modifier directement l'exécutable pour emballer le bon chargeur et les bonnes bibliothèques. Pour changer le chargeur, lancez simplement:

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

et pour vérifier:

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

et pour changer le chemin vers les bibliothèques codées en dur dans l'exécutable, vérifiez d'abord quel est le chemin actuel (vide pour moi):

$ patchelf --print-rpath wolframscript

et ajoutez-les au chemin de bibliothèque que vous avez déterminé auparavant, éventuellement séparés par des deux-points:

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

Méthode 3) Patch dans une dérivation nix

On peut reproduire plus ou moins la même chose dans une dérivation nix inspirée de skypeforlinux

Cet exemple présente également une alternative, soit vous pouvez utiliser:

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

(qui devrait être assez clair une fois que vous aurez compris la méthode "manuelle"), ou

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

Cette deuxième méthode est un peu plus subtile, mais si vous exécutez:

$ 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

vous verrez que le fichier $NIX_CC/nix-support/dynamic-linkercontient un chemin vers le chargeur ld-linux-x86-64.so.2.

Mettez derivation.nix, c'est

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

et en default.nixgros:

{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}

Compiler et exécuter avec

nix-build
result/bin/wolframscript

Méthode 4) Utilisez autoPatchElf: plus simple

Toutes les méthodes précédentes nécessitent un peu de travail (il faut trouver les exécutables, les patcher ...). NixOs a fait pour nous un "crochet" spécial qui corrige autoPatchelfHookautomatiquement tout pour vous! Il vous suffit de le spécifier (native)BuildInputset nix fait la magie.

{ 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éthode 5) Utilisez FHS pour simuler un shell Linux classique et exécutez manuellement les fichiers

Certains logiciels peuvent être difficiles à empaqueter de cette façon car ils peuvent dépendre fortement de la structure de l'arborescence des fichiers FHS ou peuvent vérifier que le binaire est inchangé. Vous pouvez ensuite également utiliser buildFHSUserEnv pour fournir une structure de fichiers FHS (légère, en utilisant des espaces de noms) pour votre application. Notez que cette méthode est plus lourde que les méthodes basées sur les correctifs et ajoute un temps de démarrage important, donc évitez-la si possible

Vous pouvez soit simplement générer un shell, puis extraire manuellement l'archive et exécuter le fichier, soit empaqueter directement votre programme pour le FHS. Voyons d'abord comment obtenir un shell. Mettez dans un fichier (disons fhs-env.nix) ce qui suit:

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

et courir:

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

Vous obtiendrez alors un bash dans un linux plus standard, et vous pouvez exécuter des commandes pour exécuter votre exécutable, comme:

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

Si vous avez besoin de plus de bibliothèques / programmes en tant que dépendances, ajoutez-les simplement multiPkgs(pour toutes targetPkgsles arches prises en charge) ou (pour l'arc actuel uniquement).

Bonus: vous pouvez également lancer un shell fhs avec une commande en ligne, sans créer de fichier spécifique:

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

Méthode 6) Utilisez FHS pour simuler un shell Linux classique et empaquetez les fichiers à l'intérieur

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

Méthode 7) à vapeur

Avec, buildFHSUserEnvvous pouvez exécuter de nombreux logiciels, mais vous devrez spécifier manuellement toutes les bibliothèques requises. Si vous voulez une solution rapide et que vous n'avez pas le temps de vérifier précisément quelles sont les bibliothèques requises, vous voudrez peut-être essayer steam-run(malgré son nom, il n'est pas directement lié à Steam et ne contient que beaucoup de bibliothèques), ce qui est comme buildFHSUserEnvavec beaucoup de bibliothèques communes préinstallées (certaines d'entre elles peuvent être non libres comme steamrtcela contient du code nvidia, merci simpson!). Pour l'utiliser, il suffit d'installer steam-run, puis:

steam-run ./wolframscript

ou si vous voulez un shell complet:

steam-run bash

Notez que vous devrez peut - être ajouter nixpkgs.config.allowUnfree = true;(ou whitelist ce paquet spécifique ) si vous voulez l' installer avec nixos-rebuild, et si vous voulez exécuter / installer avec nix-shell/ nix-envvous avez besoin de mettre { allowUnfree = true; }en ~/.config/nixpkgs/config.nix.

Il n'est pas facile de "remplacer" des packages ou des bibliothèques dans nix-shell, mais si vous souhaitez créer un wrapper autour de votre script, vous pouvez soit créer manuellement un script wrapper:

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

ou l'écrire directement dans une dérivation nixos:

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

ou si vous commencez à partir du .deb (ici j'ai utilisé à la makeWrapperplace):

{ 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 vous êtes trop fatigué pour écrire l'habituel default.nix, vous pouvez courir directement nix-build -E "with import <nixpkgs> {}; callPackage ./derivation.nix {}")

Méthode 8) Utilisation de conteneurs / Docker (beaucoup plus lourd)

FAIRE

Méthode 9) Comptez sur flatpack / appimage

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

appimage-run: pour tester avec, ex, musescore

Sources ou exemples

tobiasBora
la source