Executable

在 Nixos 上執行非 nixos 執行檔的不同方法

  • March 4, 2022

在 NixO 上執行非 nixos 執行檔有哪些不同的方法?我還想看看手動方法。

簡潔版本

又快又髒:確保steam-run已安裝(奇怪的名字,它與蒸汽無關),例如nix-shell -p steam-run,然後:

$ steam-run ./your-binary

這是一個更長更詳細的解釋,以及各種方法,通常不那麼臟。

長版

這裡有幾種方法(手動方法主要用於教育目的,因為大多數情況下編寫適當的推導更好)。我根本不是專家,我做這個列表也是為了學習 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

裝載機在這裡/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

(確保./在腳本名稱之前使用,並且只保留庫的目錄。如果您有多個庫,只需使用冒號連接路徑)

方法2)臟手動方法,有更新檔

安裝後(with nixenv -ior in your configuration.nixpatchelf,您也可以直接修改執行檔以打包好的載入器和庫。要更改載入程序,只需執行:

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 派生中打更新檔

我們可以在受skypeforlinux啟發的 nix 派生中復製或多或少相同的東西

此範例還提供了一種替代方法,您可以使用:

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包含 loader 的路徑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 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";
}

並執行:

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

然後,您將在外觀更標準的 linux 中獲得 bash,並且可以執行命令來執行執行檔,例如:

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 shell,而無需創建特定文件:

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

方法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

或者如果你想要一個完整的外殼:

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)依賴flatpack/appimage

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

appimage-run : 測試,例如,musescore

來源或例子

引用自:https://unix.stackexchange.com/questions/522822