你的第一个推导

你的第一个推导

如何编写一个简单的推导来打包 nix 程序以及如何创建 PR 将其包含在 nixpkgs 中?

(我写这篇文章是因为我找不到简单的解释)

答案1

注意:这个答案尚未完全完成,但它已经是一个很好的起点。我计划稍后添加更多特定于语言的内容(或者可能为每种语言创建一个问题,也保持这个答案......“”“合理”“”简短)。

以下是一些参考:

  • 手册中的快速入门: 我们将在这里详细介绍
  • 尼克斯药丸(尤其是第 6 节):它们很棒,但采用自下而上的方法,首先解释我觉得令人困惑的 nix 的所有内部结构……您可能不需要学习所有这些来编写您的第一个推导。这里我们将采取自上而下的方法,即给出大多数用户想要直接使用的功能,并解释它之后的工作原理。
  • 的文档标准环境这非常好,但包含很多信息和很少(不完整)的示例
  • 其他我的回答,但特定于构建预编译的二进制文件

你的第一个推导

通俗地说,推导是构建程序的秘诀。当你做饭时,你需要一些成分(又名 nix 中的来源和依赖项)以及一些将成分组合成蛋糕的步骤(又名程序......)。

一个简单的 C 程序

让我们从一个简单的例子开始,这是我能想象到的最简单的 C 程序。您可以将其写入program.c您喜欢的任何文件夹中的文件(对于本例):

#include <stdio.h>

int main() {
   printf("Hello, World!\n");
   return 0;
}

推导

然后,我们需要告诉 nix 如何编译这个程序。所以创建一个文件derivation.nix

{ stdenv }:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  src = ./.;

  nativeBuildInputs = [ ];
  buildInputs = [ ];

  buildPhase = ''
    gcc program.c -o myprogram
  '';

  installPhase = ''
    mkdir -p $out/bin
    cp myprogram $out/bin
  '';
}

这描述了一个函数,其中输入是依赖项(蛋糕的“成分”;这里仅stdenv提供有用的实用程序),并输出一个推导,感谢stdenv.mkDerivation:非正式地,你可以想象这个过程将输出一个包含所有编译文件的文件夹。我提供了mkDerivation一些信息:

  • 源:这里的源位于当前文件夹中,但您也可以从网络获取源,我们稍后会看到
  • nativeBuildInputs编译程序所需的依赖项(gcc默认情况下始终包含...因此您无需在此处指定任何内容)
  • buildInputs运行程序所需的依赖项(您通常会将库放在这里)
  • buildPhase构建程序的说明(它是一个 bash 脚本)。在此阶段开始时,您将被放入包含源代码的文件夹中
  • installPhase描述如何“安装”程序的说明(见下文)。

实际上还有更多阶段(解压缩源、修补、配置……),但本示例不需要它们。

正在installPhase做什么?

安装阶段在这里说明最终的可执行文件/库/资产/…应该位于哪里。在典型的 Linux 环境中,二进制文件通常被复制到/bin/usr/bin/usr/local/bin、 库 或/lib/lib64资产/share...... 当所有程序将自己的东西放在同一个地方时,它很快就会变得一团糟。

在 Nix 中,所有程序都在类似的路径中拥有自己的文件夹(该路径的值在 中/nix/store/someUniqueHash-programName-version设置为),然后二进制文件转到,库到,资产到...再现典型的 Linux 文件夹层次结构。因此,如果您不确定应该将文件放在哪里,您肯定需要检查将其放在普通 Linux 发行版中的位置,并在路径前面添加(很少有例外,就像我们使用而不是因为没有更多原因)有一个文件夹)。请注意,许多构建系统(cmake ...)都有一个变量,例如表示程序应安装在哪里。:通常可能是or ,这会将二进制文件安装到等。在这种情况下,我们通常可以简单地设置并运行通常的编译命令。当您安装程序时,Nix 将正确创建指向已安装软件文件的链接,例如在 NixOs 中,全局安装的二进制文件链接在$outinstallPhase$out/bin$out/lib$out/share$out/$out/bin$out/usr/local/binlocalPREFIXPREFIX//usr/localPREFIX/binPREFIX=$out/run/current-system/sw/bin

$ ls /run/current-system/sw/bin -al | grep firefox
lrwxrwxrwx 1 root root   70 janv.  1  1970 firefox -> /nix/store/152drilm2qhjimzfx8mch0hmqvr27p29-firefox-99.0.1/bin/firefox

因此,在我们的示例中,在安装阶段,我们只需要创建文件夹$out/bin并复制在复制阶段获得的二进制文件......而这正是我们所做的!

我怎样才能尝试呢?

要尝试它,您仍然需要指定从哪里获取依赖项(同样,当您烹饪蛋糕时,您应该首先拜访您最喜欢的农民购买一些鸡蛋)。因此,创建另一个文件default.nix(名称在这里很重要,因为nix将首先查找该文件)包含

{ pkgs ? import <nixpkgs> {} }:
pkgs.callPackage ./derivation.nix {}

在这里,您基本上告诉 nix 使用通道<nixpkgs>来获取依赖项,并将callPackage正确填充derivation.nix.

然后,只需运行

$ nix-build

最后,您应该有一个新文件夹result,并且该文件夹链接到$out您的派生文件夹:

$ ls -al | grep result
lrwxrwxrwx  1 leo  users  55 sept. 13 20:59 result -> /nix/store/xi0hx472hzykl6xjw0hnmh0zjyp6sc52-program-1.0

然后您可以使用以下命令执行二进制文件:

$ ./result/bin/myprogram
Hello, World!

恭喜,您完成了第一次推导!

下面我们将看到如何打包更复杂的应用程序。但在此之前,让我们看看如何安装该软件包并为 nixpkgs 做出贡献。

我可以将其安装在我的系统上吗?

您当然可以安装这个派生。复制您的文件(除非default.nix不需要)/etc/nixos并将已安装软件包的列表更改为:

environment.systemPackages = with pkgs; [
  (callPackage ./derivation.nix {})
]

干得好!

您还可以使用命令式将其安装在任何系统上

$ nix-env -i -f default.nix

我如何将我的推导提交给 nixpkgs?

nixpkgs 项目中的所有包表达式都位于https://github.com/NixOS/nixpkgs你可以在那里添加你自己的包!为此,首先分叉(以执行拉取请求)并克隆您的存储库。然后复制进去,derivation.nix其中pkgs/CATEGORY/PACKAGE/default.nix根据CATEGORY你的程序的应用范围适当选择,PACKAGE就是你的程序的名称。

当然,nixpkgs 存储库不包含程序的源代码,因此您应该更改源属性以指向外部源(见下文)。

然后,nixpkgs 中可用的所有程序的列表位于,pkgs/top-level/all-packages.nix因此您应该添加一行:

  myprogram = callPackage ../CATEGORY/PACKAGE { };

在此文件中(程序按字母顺序排序)。要测试它,请转到存储库的根目录并调用

$ nix-build -A myprogram

result它应该像以前一样编译您的程序并创建一个文件夹来测试它。

完成后,提交您的工作并将其作为拉取请求提交!

如果您不熟悉 git 或想了解更多详细信息,您可能会喜欢这个帖子https://discourse.nixos.org/t/how-to-find-needed-librarys-for-lined-source-bin-applications/39118/43?u=tobiasbora

如果来源是在线的怎么办?

大多数时候,您会尝试下载在线托管的源。没问题,只需更改您的src属性,例如,如果您从 github 下载(请参阅获取器列表这里):

{ stdenv, lib, fetchFromGitHub }:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  # For https://github.com/myuser/myexample
  src = fetchFromGitHub {
    owner = "myuser";
    repo = "myexample";
    rev = "v${version}"; # If there is a release like v1.0, otherwise put the commit directly 
    sha256 = ""; # <-- dummy hash: after the first compilation this line will give an error and the correct hash. Replace lib.fakeSha256 with "givenhash". Or use nix-prefetch-git. On older nix, this might fail, use sha256 = lib.fakeSha256; instead.
  };

  buildPhase = ''
    gcc program.c -o myprogram
  '';

  installPhase = ''
    mkdir -p $out/bin
    cp myprogram $out/bin
  '';
}

确保sha256使用您自己的哈希更改该行(需要验证下载的文件是否正确)。lib.fakeSha256是一个虚拟哈希,因此第一次编译时会给出错误,指出哈希是错误的,而它是truehash。因此,用这个值替换哈希(还有类似的工具,nix-prefetch-git但我不得不承认我不使用它们)。警告:如果您使用缓存中已有的另一个程序的哈希值,它不会给出任何错误,而是会很好地峰值另一个包的源!

另请注意,nix 会自动尝试对源代码执行正确的操作,特别是它会自动解压下载的压缩文件

  src = fetchurl {
    url = "http://example.org/libfoo-source-${version}.tar.bz2";
    sha256 = "0x2g1jqygyr5wiwg4ma1nd7w4ydpy82z9gkcv8vh2v8dn3y58v5m";
  };

如何使用库

现在,让我们使用一个库(本示例中的 ncurses)使程序稍微复杂一些。我们将使用ncurseshello-world 程序:

#include <ncurses.h>

int main(int argc, char ** argv)
{
    initscr(); // init screen and sets up screen
    printw("Hello World"); // print to screen
    refresh(); // refreshes the screen
    getch(); // pause the screen output
    endwin(); // deallocates memory and ends ncurses
    return 0;
}

如果你像上面那样直接编译这个程序,你会得到一个错误

program.c:1:10: fatal error: ncurses.h: No such file or directory

这是预期的,因为我们没有添加 ncurses 作为依赖项。为此,请在(空格分隔)列表中添加buildInputsncurses(您还必须将其添加到输入依赖项的第一行中):这样做将确保buildInputs编译器搜索的程序的二进制文件可用子目录中的头文件include...同时更新编译命令-lncurses

{ stdenv, ncurses }:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  src = ./.;

  buildInputs = [
    ncurses
  ];

  buildPhase = ''
    gcc -lncurses program.c -o myprogram
  '';

  installPhase = ''
    mkdir -p $out/bin
    cp myprogram $out/bin
  '';
}

像以前一样编译并运行程序,就是这样!

调试程序,第 1 部分:nix-shell

nix-build有时使用as 来调试程序可能会很烦人,nix-build因为它不会缓存编译:每次失败时,它都会在下一次从头开始编译(这是确保可重复性所必需的)。然而在实践中这可能有点烦人...... nix-shell已经创建(也)来解决这个问题。如果您运行gcc命令来编译上述文件,它将直接失败,因为 gcc 和 ncurses 库没有全局安装(这是一个功能,例如它允许多个项目使用同一库的不同版本)。要创建安装此程序的 shell,只需运行nix-shell,它会自动检查程序的依赖项:

$ nix-shell
$ gcc -lncurses program.c -o myprogram
$ ./myprogram

稍后我们会看到更高级的用法nix-shell

使用默认阶段和挂钩节省时间

编译程序的方法通常是相同的,因为许多程序都是简单地使用以下方式编译的:

$ ./configure --prefix=$out
$ make
$ make install

因此,nix 默认情况下会尝试上述命令(以及更多的尝试修补、测试......),这就是为什么 nixpkgs 中的许多程序实际上并不需要编写任何阶段。

大多数阶段都是真正可配置的:例如,您可以启用/禁用阶段的某些部分,提供一些参数,例如makeFlags = [ "PREFIX=$(out)" ];向 makefile 添加标志……提供了这些阶段的完整文档在手册中,更具体地说是在本小节。如果你确实想检查正在运行的内容,可以检查genericBuild文件中的函数pkgs/stdenv/generic/setup.sh然后调用文件中上面写入的默认阶段,除非它们被推导覆盖。您还可以直接从 中读取使用的代码,nix-shell我们稍后会看到。

请注意,这些默认阶段也可以被依赖项覆盖。例如,如果您的程序使用cmake,添加nativeBuildInputs = [ cmake ];将自动调整配置阶段以使用 cmake (这也可以按照文档进行配置这里)。类似的行为也会发生在 scons、ninja、meson 上……更一般地说,nix 定义了许多“钩子”,这些“钩子”将在给定阶段之前或之后运行,以便修改构建过程。只要将它们包含在内nativeBuildInputs就足以触发它们。大多数钩子都有记录这里,其中您有:

  • autoPatchelfHook自动修补(通常是专有的)二进制文件,使它们可以在 nix 中使用(另请参阅我的其他答案这里

例如,我们可以在 (ncurse) 程序中使用 CMake,如下所示:创建一个CMakeLists.txt包含常用 cmake 规则的文件来编译程序:

cmake_minimum_required(VERSION 3.10)

# set the project name
project(myprogram)

# Configure curses as a dependency
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})

# add the executable
add_executable(myprogram program.c)

# Link the curses library
target_link_libraries(myprogram ${CURSES_LIBRARIES})

# Explains how to install the program
install(TARGETS myprogram DESTINATION bin)

现在可以大大简化我们的derivation.nix

{ stdenv, ncurses, cmake }:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  src = ./.;

  buildInputs = [
    ncurses
    cmake
  ];

}

使用 nix-shell 和 nix 内部结构调试程序:第 2 部分

注意:本节不是理解其余部分所必需的,您可以安全地跳过它。

我们在上面看到了如何nix-shell将我们放入具有所有必需依赖项的 shell 中,以通过利用缓存来节省编译时间。在这个 shell 中,当然可以像以前一样运行常用命令来编译程序,但有时最好运行与 nix 构建器运行的命令完全相同的命令。

这也是了解更多有关 nix 内部结构的机会(我们也参考尼克斯丸了解更多详情以及到维基百科)。当您编写派生时,nix 将从中派生出一个.drv文件,该文件以简单的 json 格式解释如何构建包。

要查看该文件,您可以运行:

$ nix-shell
# (or "nix-shell -A myprogram" if you run it from nixpkgs)
$ nix show-derivation $(nix-instantiate | sed 's/!.*//')
{
  "/nix/store/4ja3vvab4wswalczr7k0lw17dxb69nf7-program-1.0.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/qv8s0lm7w0az90xjc90dy7rvjqmic9zz-program-1.0"
      }
    },
    "inputSrcs": [
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
      "/nix/store/zrpp5wmrq39ylqy73pbk3plvw5sx59vh-example"
    ],
    "inputDrvs": {
      "/nix/store/1av43alhcb8a894sz2cnnf9aldfdyb0h-stdenv-linux.drv": [
        "out"
      ],
      "/nix/store/6pj63b323pn53gpw3l5kdh1rly55aj15-bash-5.1-p16.drv": [
        "out"
      ],
      "/nix/store/p6y4zvhi9vjg8h7hli0ix9jxkl225ahk-ncurses-6.3-p20220507.drv": [
        "dev"
      ],
      "/nix/store/w6jf92i16rghx0jr4ix33snq4d237l8i-cmake-3.24.0.drv": [
        "out"
      ]
    },
    "system": "x86_64-linux",
    "builder": "/nix/store/1b9p07z77phvv2hf6gm9f28syp39f1ag-bash-5.1-p16/bin/bash",
    "args": [
      "-e",
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"
    ],
    "env": {
      "buildInputs": "/nix/store/kn8gbpi8bfxkzg6slyskz4y0d2pkl0xk-ncurses-6.3-p20220507-dev /nix/store/xjg2fzw513iig1cghd4mvcq5fh2cyv4y-cmake-3.24.0",
      "builder": "/nix/store/1b9p07z77phvv2hf6gm9f28syp39f1ag-bash-5.1-p16/bin/bash",
      "cmakeFlags": "",
      "configureFlags": "",
      "depsBuildBuild": "",
      "depsBuildBuildPropagated": "",
      "depsBuildTarget": "",
      "depsBuildTargetPropagated": "",
      "depsHostHost": "",
      "depsHostHostPropagated": "",
      "depsTargetTarget": "",
      "depsTargetTargetPropagated": "",
      "doCheck": "",
      "doInstallCheck": "",
      "mesonFlags": "",
      "name": "program-1.0",
      "nativeBuildInputs": "",
      "out": "/nix/store/qv8s0lm7w0az90xjc90dy7rvjqmic9zz-program-1.0",
      "outputs": "out",
      "patches": "",
      "propagatedBuildInputs": "",
      "propagatedNativeBuildInputs": "",
      "src": "/nix/store/zrpp5wmrq39ylqy73pbk3plvw5sx59vh-example",
      "stdenv": "/nix/store/bj5n3k01mq8bysw0rcdm7jxvhc620pd3-stdenv-linux",
      "strictDeps": "",
      "system": "x86_64-linux",
      "version": "1.0"
    }
  }
}

确切的输出并不重要,但请注意有几个重要部分:首先,派生指定输出文件夹、源和依赖项、构建期间可用的一些环境变量以及 nix-shell 自动填充的我们:看到了吗"out": …?由于以下原因,您已经正确配置了它nix-shell

$ echo $out
/nix/store/qv8s0lm7w0az90xjc90dy7rvjqmic9zz-program-1.0

更重要的是这些行:

    "builder": "/nix/store/1b9p07z77phvv2hf6gm9f28syp39f1ag-bash-5.1-p16/bin/bash",
    "args": [
      "-e",
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"
    ],

这意味着要生成输出,nix 将简单地使用参数运行构建器/nix/store/…/bin/bash(这里只是 bash 解释器)-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh

这个文件非常简单:

$ cat /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh
source $stdenv/setup
genericBuild

如果你输入

$ cat $stdenv/setup

你会发现它完全等于pkgs/stdenv/generic/setup.sh配置默认阶段的文件!

因此,在 中nix-shell,您可以使用类似的方法一次运行所有阶段(创建不同的$out文件夹允许您不以只读方式写入/nix/store):

cd empty_directory # important to make sure "source" folder is not existing, otherwise you get an error like "unpacker appears to have produced no directories". Sources will be unpacked in a subdirectory, and it must be removed every time you restart the download process (otherwise we get the above error).
export out=/tmp/out # Create a temporary folder to put the output of the derivation
set -x # Optional: to display all the command lines, useful to debug sometimes
source $stdenv/setup # In order to load the default phase of the derivation
set +e # Do not quit the shell on error/Ctrl-C ($stdenv/setup adds a "set -e")
genericBuild # start the build process.

您还可以通过将最后一行替换为以下内容来指定要运行的几个阶段:

phases="buildPhase" genericBuild

要获取阶段列表,您可以执行以下操作:

echo "$phases"

如果它为空,则默认值例如通过

$ typeset -f genericBuild | grep 'phases='
phases="${prePhases:-} unpackPhase patchPhase ${preConfigurePhases:-}             configurePhase ${preBuildPhases:-} buildPhase checkPhase             ${preInstallPhases:-} installPhase ${preFixupPhases:-} fixupPhase installCheckPhase             ${preDistPhases:-} distPhase ${postPhases:-}"

封装其他语言

上面提供的说明当然适用于许多语言和情况,但某些语言提供了一些其他工具来处理环境变量和依赖项方面的自身要求(例如,我们不能真正使用它pip来安装 python 依赖项)。很难在此页面上列出所有现有语言,因此这里有一些需要遵循的通用建议:

  • nixpkgs手册基本上每种语言包含一个部分,因此通常是一个很好的起点
  • 尼克斯维基有时包含给定语言的附加信息。检查是否有帮助
  • nixpkgs 仓库包含数千个程序……肯定有人在像您要打包的程序这样的程序之前打包。获得灵感,使用在线搜索(有时似乎会错过一些条目)或rg(更好的 grep)在本地副本中搜索,以使用您想要使用的工具查找派生。

不过,为了简单起见,我将在下面列出一些您可能经常遇到的情况。

如何打包(通常是专有的)二进制文件

我已经做了一个相当广泛的答案这里。您当然对解决方案 4 (autoPatchElf) 或 5-6 (buildFHSUserEnv) 感兴趣...基本上将您的二进制文件复制到$out/bin,如果您幸运的话,添加autoPatchelfHook您的nativeBuildInputs应该就足够了(如果程序有资产,您也可以将其复制到$out/opt并放入$out/bin一些调用 ) 中的程序的链接或脚本$out/opt

如何打包shell脚本

让我们考虑一下该文件myshellscript.sh

#!/usr/bin/bash

echo "Hello, world"

只需使用

{ stdenv }:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  src = ./.;

  installPhase = ''
    mkdir -p $out/bin
    cp myshellscript.sh $out/bin
    chmod +x $out/bin/myshellscript.sh # not needed if the file is already executable
  '';
}

patchShebangsAutobash 脚本将自动由钩子修补默认出现在修复阶段

进一步阅读以了解如何使用平凡的构建器使这个推导变得更小!

包装器,或如何添加可执行文件

假设我们的包需要一些可执行文件才能工作,例如cowsay.因为 nix 试图保持包之间的“隐秘性”(又名纯度)以限制冲突,正如精美解释的那样这里(也许不同的程序需要不同版本的cowsay),您不能假设它将cowsay“可用”,即存在于$PATH环境变量中。因此,您需要cowsay在调用程序之前添加此变量。这是通过所谓的“包装器”替换原始程序来完成的,该包装器将$PATH在调用实际程序之前设置(以及更多环境变量,如果需要)。

请注意,我们将看到后面的工具可以使简单的 bash 脚本的此步骤变得更加简单,但是包装器在许多情况下都很有用,现在学习如何使用它们肯定不会浪费时间。

那么让我们打包这个myshellscript.sh脚本:

#!/usr/bin/bash

cowsay "My first wrapper!"

使用这个derivation.nix

{ lib, stdenv, cowsay, makeBinaryWrapper}:
stdenv.mkDerivation rec {
  name = "program-${version}";
  version = "1.0";

  src = ./.;
  nativeBuildInputs = [
    makeBinaryWrapper # You can also use makeWrapper to use a bash wrapper, but this won't be compatible with MacOs that expects binary loaders
  ];
  buildInputs = [
    cowsay
  ];

  installPhase = ''
    mkdir -p $out/bin
    cp myshellscript.sh $out/bin
    chmod +x $out/bin/myshellscript.sh
    wrapProgram $out/bin/myshellscript.sh \
      --prefix PATH : ${lib.makeBinPath [ cowsay ]}
  '';
}

请注意我们如何添加输入cowsay以及如何使用以下命令创建包装器:

    wrapProgram $out/bin/myshellscript.sh \
      --prefix PATH : ${lib.makeBinPath [ cowsay ]}
  '';

以添加cowsay到路径中。现在,如果您nix-build(不要忘记通常的default.nix文件),您可以看到./result/bin/myshellscript.sh现在是一个二进制文件(您仍然可以以某种方式读取less)...因为很难看出这个文件到底在做什么,您可能想使用makeWrapper而不是使用makeBinWrapper但请注意,出于“安全”原因,它在 MacO 中不起作用。在这里你会读到类似的内容:

$ cat result/bin/myshellscript.sh
#! /nix/store/1b9p07z77phvv2hf6gm9f28syp39f1ag-bash-5.1-p16/bin/bash -e
PATH=${PATH:+':'$PATH':'}
PATH=${PATH/':''/nix/store/mrl0n0kphz0xwvv8qbk2xyz2x1pr2f76-cowsay-3.04/bin'':'/':'}
PATH='/nix/store/mrl0n0kphz0xwvv8qbk2xyz2x1pr2f76-cowsay-3.04/bin'$PATH
PATH=${PATH#':'}
PATH=${PATH%':'}
export PATH
exec -a "$0" "/nix/store/xrz4cv51nd8n1bawfw5i6vd4yizzmajb-program-1.0/bin/.myshellscript.sh-wrapped"  "$@" 

这段代码有点复杂,但它的作用基本上是在路径的开头添加cowsay的二进制文件,然后执行$out/bin/.myshellscript.sh-wrappedwrapProgram工具已移动到的shell文件。

现在是时候测试一下了:

$ ./result/bin/myshellscript.sh
 ___________________ 
< My first wrapper! >
 ------------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

好的!请注意,您可以找到修改包装器的各种选项这里

由于简单的构建器,推导变得更短

有时,编写安装阶段、包装器等可能会有点烦人stdenv.mkDerivation……因此创建了简单的构建器来包装stdenv.mkDerivation成更简单的函数。他们被记录在案在手册中。我们不会遍历所有这些(其中一些用于创建新文件、脚本、合并派生......),但我们将使用它来简化我们的代码运行cowsay

这样我们就可以简单地使用这个推导:

{ lib, stdenv, cowsay, writeShellApplication }:
writeShellApplication {
  name = "mycowsay";

  runtimeInputs = [ cowsay ];

  text = ''
    cowsay "My first wrapper!"
  '';
}

它将创建一个 bash 脚本,其中$out/bin/mycowsay包含适当的$PATH基于runtimeInputs.如果您更喜欢像以前一样在外部文件中编写脚本,您可以这样做:

text = builtins.readFile ./myshellscript.sh;

如何打包python脚本

TODO,但请参阅相关主题:

  postFixup = ''
    wrapProgram "$out/bin/mssql-cli" \
      --prefix PYTHONPATH : "$PYTHONPATH"
  '';

在 , 二进制文件内buildPythonPackage(但不是脚本)应该(测试)自动包装。 (我不太喜欢这个,但wrapPythonPrograms我认为只适用于二进制文件,所以我创建了这个问题

如何打包 GTK 应用程序

去做

如何打包QT应用程序

去做

相关内容