web-dev-qa-db-fra.com

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

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

20
tobiasBora

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

Donc, le problème principal est que l'appel exécutable d'abord un chargeur, puis a besoin de certaines bibliothèques pour travailler et Nixos mettez à la fois le chargeur et les bibliothèques dans /nix/store/.

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

  • le manuel complet: intéressant à des fins éducatifs, et à comprendre ce qui se passe, mais c'est tout (ne les utilisez pas dans la pratique, car rien n'empêchera les dérivations utilisées pour être des ordures recueillies 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 faire le point sur la bonne bibliothèque directement.
  • les méthodes basées sur la FHS, qui simulent essentiellement une "Normal Linux" (plus lourde à exécuter que la version patchée, cela devrait donc être évité si possible).

Je recommanderais la méthode 4 avec autoPatchelfHook pour une configuration réelle et appropriée, et si vous n'avez pas de temps et que vous souhaitez simplement exécuter un binaire en une ligne, vous pouvez être intéressé par le rapide et sale Solution 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

Ici le chargeur est /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 trouver pour trouver les bibliothèques requises par votre programme, 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 sont trouvées, 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

Bon. Maintenant, nous devons simplement exécuter le programme avec le LD_LIBRARY_PATH Configuré pour pointer sur ce fichier et appelez 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 ne conservez que le répertoire des bibliothèques. Si vous avez plusieurs bibliothèques, utilisez simplement Concat le chemin avec des points de vue)

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

Après l'installation (avec nixenv -i ou dans votre configuration.nix) patchelf, vous pouvez également modifier directement l'exécutable pour emballer la bonne chargeur et les bibliothèques. Pour changer le chargeur vient de courir:

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

et de vérifier:

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

et pour changer le chemin d'accès aux bibliothèques codées dans l'exécutable, vérifiez d'abord ce qui est le RPATH actuel (vide pour moi):

$ patchelf --print-rpath wolframscript

et appendez-les à la piste de la bibliothèque que vous avez déterminée auparavant, éventuellement séparées de colons:

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

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

Nous pouvons reproduire plus ou moins la même chose dans une dérivation Nix inspirée par Skypeforlinux

Cet exemple présente également une alternative, que vous puissiez 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 comprenez 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-linker contient un chemin vers le chargeur ld-linux-x86-64.so.2.

Mettre en 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.nix mettre:

{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}

Compiler et courir 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 (vous devez trouver les exécutables, les patcher ...). Nixos a fait pour nous un "crochet" spécial autoPatchelfHook qui corrige automatiquement tout pour vous! Vous avez juste besoin de le spécifier dans (native)BuildInputs, et 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 une coque Linux classique et exécuter manuellement les fichiers

Certains logiciels peuvent être difficiles à emballer de cette façon, car ils peuvent s'appuyer fortement sur le [~ # ~ # ~ ~ ~] Structure d'arborescence de fichier, ou peut vérifier que les binaires sont inchangées. Vous pouvez alors utiliser également Buildfhsuserenv pour fournir une structure de fichiers FHS (légère, à l'aide d'espaces de noms) pour votre application. Notez que cette méthode est plus lourde que les méthodes basées sur le patch et ajoutent du temps de démarrage significatif, alors évitez-le lorsque cela est possible

Vous pouvez simplement apparaître une coquille, puis extraire manuellement les archives et exécuter le fichier, ou diriger directement votre programme pour la FHS. Voyons d'abord comment obtenir une coquille. Mettre dans un fichier (dire 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 ensuite une 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 comme des dépendances, ajoutez-les simplement à multiPkgs (pour tous les arcs pris en charge) ou targetPkgs (pour la voûte actuelle uniquement).

Bonus: Vous pouvez également lancer une coquille FHS avec une commande d'une 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 une coque Linux classique et emballer les fichiers à l'intérieur

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

Méthode 7) Run-Run

Avec buildFHSUserEnv Vous pouvez exécuter des logiciels de lot, mais vous devrez spécifier manuellement toutes les bibliothèques requises. Si vous souhaitez une solution rapide et que vous n'avez pas le temps de vérifier avec précision quelles sont les bibliothèques requises, vous voudrez peut-être essayer Steam-run (Malgré le nom, il n'est pas lié directement avec Steam et emballe juste de nombreux bibliothèques), qui est comme buildFHSUserEnv avec beaucoup de bibliothèques communes préinstallé (certaines d'entre elles peuvent être non libres comme steamrt qui contient du code NVIDIA, merci Simpson!). Pour l'utiliser, installez simplement Steam-run, puis:

Steam-run ./wolframscript

ou si vous voulez une coquille complète:

Steam-run bash

Notez que vous devrez peut-être ajouter nixpkgs.config.allowUnfree = true; (ou whitelist ce package spécifique ) Si vous souhaitez l'installer avec nixos-rebuild, et si vous voulez exécuter/l'installer avec nix-Shell/nix-env Vous devez mettre { allowUnfree = true; } dans ~/.config/nixpkgs/config.nix.

Il n'est pas facile de "écraser" des paquets ou des bibliothèques à Nix-Shell, mais si vous souhaitez créer une enveloppe autour de votre script, vous pouvez créer manuellement un script wrapper:

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

ou écrivez-le 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é makeWrapper plutôt):

{ 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, musclus

Sources ou exemples

20
tobiasBora