如何为 Raku CLI 使用包装脚本

问题描述

我想知道在 Raku 中创建 CLI 应用程序时推荐哪些步骤(例如 shebang 行和包装脚本)。我对将与 Zef 一起安装的脚本和将单独分发的脚本的信息感兴趣。

文档提供了示例 frobnicate 程序:

# inside "frobnicate.raku" 
sub MAIN(Str $file where *.IO.f,Int  :$length = 24,Bool :$verbose) { #`(body omitted)}

我可以用 raku frobnicate.raku 运行——一个很好的脚本解决方案,但对真正的程序来说不是很好。

如果我希望它更像一个标准程序,我可以创建一个 frobnicate 文件,如下所示:

#!/usr/bin/env raku
sub MAIN(Str $file where *.IO.f,Bool :$verbose) { #`(body omitted)}

我可以使用 chmod +x 使该文件可执行并将其移动到我的 $PATH 中的目录中;然后我可以使用命令 frobnicate 进行 frobnicate。到目前为止,所有这些都非常合理,就像任何其他脚本语言一样。

然而,这些都没有利用 Raku 的编译。因此,在 MAIN 中进行更多处理的 CLI 应用程序可能会变慢(即使生成 --help 输出也是如此)。因此,下一步是允许预编译应用程序,但我不太确定该怎么做。

与泽夫

当我查看使用 Zef 安装 Raku 程序后执行的脚本时,它们几乎都具有以下形式:

#!/usr/bin/env perl6
sub MAIN(:$name,:$auth,:$ver,*@,*%) {
    CompUnit::RepositoryRegistry.run-script("frobnicate",:$name,:$ver);
}

而且,当我检查包的来源时,此 CompUnit::RepositoryRegistry.run-script 行不存在。所以我猜Zef会添加它?如果是这样,在编写我的脚本时我需要做些什么来确保 Zef 将使用这个包装器并且包装器可以工作?

This comment 有助于了解正在发生的事情,但我仍然不确定我是否 100% 明白)。

没有Zef

我想编写用户可以运行的脚本,无需通过 Zef 安装它们(尽管这是我推荐的安装方法)。有没有办法在没有 Zef 的情况下使用上面显示的 run-script 方法?或者我应该做以下类似的事情,我也看到过:

#!/usr/bin/env raku
use Frobnicate::CLI

(然后将实际功能移动到 Frobnicate/CLI.rakumod 文件中)。

如果我走这条路,我需要指导用户下载上面的 frobnicate 包装脚本和 Frobnicate/CLI.rakumod 文件,对吗? (也就是说,无法在单个文件中执行此操作,是吗?)

假设我确实需要让我的用户下载两个文件,我应该让他们在哪里安装这些文件? frobnicate 需要进入其 PATH 中的目录,但是 Frobnicate/CLI.rakumod 呢?是否需要将其复制到他们的 Raku 模块搜索路径中(如果需要,什么命令会向他们显示该路径是什么)?或者我可以以某种方式修改 frobnicate 包装器(也许通过将 raku 更改为 raku -Ilib 或类似的东西?),让他们将两者都安装到 {{ 1}}?

在把所有这些都打出来之后,我觉得我想得太多了。如果是这样,也请让我知道!无论如何,如果我能更好地理解这一点,我很乐意在文档的相关部分添加更多细节。

解决方法

所以我猜是 Zef 添加了它?

CompUnit::Repository::Installation adds it

为什么会在那里?原因之一是因为某些包装器需要管理同名的 bin 脚本,否则如果它们在同一目录中,则会发生冲突。

有没有办法在没有 Zef 的情况下使用上面显示的 run-script 方法?

run-script is for CompUnit::Repository::Installation only -- 如果你没有安装模块,那么 run-script 不会有兴趣

假设我确实需要让我的用户下载两个文件,我应该让他们在哪里安装这些文件?

推荐/惯用的方法是使用核心 raku 功能来安装东西(即使用 zef)。否则,您应该在何处放置一些代码 a) 无关紧要或 b) 将主要依赖于您的环境,您的操作系统的惯用语等。

是否需要将其复制到他们的 Raku 模块搜索路径中(如果需要,什么命令会向他们显示该路径是什么)

echo $RAKULIB 在大多数情况下应该足以显示模块搜索路径,尤其是当他们对安装路径的位置不感兴趣时​​。因此,您可以指示用户设置例如RAKULIB=$FROB_LIB_DIR 指向您的库所在的任何位置,如果您希望他们能够运行您的脚本而无需通过 raku -I../ frobnicate 手动指定它(因此他们不会将代码复制到任何特殊的地方,他们只是指向他们例如克隆你的回购)。与 $PATH 一起使用也是如此。

或者我可以以某种方式修改 frobnicate 包装器(也许通过将 raku 更改为 raku -Ilib 或类似的东西?),以便让他们将两者都安装到其 PATH 上的目录中?

我建议不要根据 $PATH 中的某个值安装东西。指示用户设置 $PATH,不要将东西安装到 $PATH

从技术上讲,您可以将 use lib '../' 添加到您的脚本中,但是在您还希望用户正常安装的脚本中使用 use lib 不太理想,因为它会在以下情况下添加未使用的、可能被劫持的模块搜索路径从这样的安装中运行。

如果您希望您的代码预编译,那么我建议将其放在一个模块中,并指示您的用户,如果他们不打算安装它,则在每次使用的基础上通过 raku -I../ ./frobnicate 调用它或者像 export RAKULIB="$FROB_LIB_DIR,$RAKULIB" 后跟 ./frobnicate 之类的东西,以获得更持久的东西。或者,如果有人最终实现了脚本的预编译,那么您可以只使用单文件方法。

,

关于这个话题的一些评论。

  1. 我认为最好使用 zef,因为 zef 也会加载依赖项。能够在不使用其他模块的情况下编写 Raku 程序已经很不寻常了,我预计随着更多 Raku 模块的开发,这种情况会变得更加不寻常。

  2. 我忘记了在我的系统上安装了哪些模块,并包含在程序中。通过在 META6.json 中指定所有内容,然后运行 ​​zef test .,可以提高确保其他人可以下载模块的机会。实际上,我发现确保这一点的最佳方法是创建一个 docker 文件并尝试在 docker 映像/容器中安装一个新模块 - 但这是另一个话题。

  3. 我发现(对于 Linux,我无法评论 Windows)如果我:

    • 在 Raku 中编写一个可执行文件(见下文),我们称之为 MyWonder
    • 将其放在 <distribution>/bin 文件夹中,
    • 给它 +x 权限,并且
    • 在分发的根 META6.json 文件中指定它
    • (发布发行版)

    然后当 zef 安装发行版时,MyWonder 将在命令行上工作(假设 zef 本身在命令行上工作,暗示 PATH 包含目录到 zef)。

  4. 调用旨在在命令行上使用或由桌面调用的程序的最佳(对我而言)方式是:

    • 将以下内容放入 MyWonder 文件(无需扩展名)
    use v6.d;
    use MyWonderLife;
    
    • 将所有功能放入 MyWonderLife.rakumod
    • 确保sub MAIN中至少有一个MyWonderLife.rakumod
    • MyWonderLife 文件中指定 META6.json(这对于将实际 MyWonderLife.rakummod 文件放在哪个目录下具有很大的灵活性;它不必是 {{1 }})
    • 为分布创建一个简单的测试 lib/,只有测试 t/basic.rakutest

    这个秘籍意味着所有功能都由 zef 预编译,因此当用户调用可执行文件时,响应速度要快得多。使用 Raku 时最慢的部分是编译程序。通过使用 zef 安装,这在安装过程中完成一次。所有语言的程序安装都很慢,因此 Raku 程序不会让用户想知道在使用它时发生了什么。

    其次,可以使用多个 use-ok 'MyWonderLife; 来处理各种调用情况,并使用现在可以处理的命令行选项范围。虽然这显然在任何脚本中都是可能的,但将它们全部放在 multi sub MAIN 文件中似乎(对我来说)更自然。

    我发现在安装模块时需要进行大量测试,因此我开始将大部分开发和维护测试转移到 .rakumod,并且仅在 {{1} 进行简单的安装测试}}。

    最后,有了这个秘籍(并假设您在 xt/ 文件中为发行版指定了名称 MyWonderLife,t/ 的安装说明,假设可以不带参数调用该程序简单地说:

    使用zef安装MyWonderLife,例如,

    META6.json

    并在命令行上使用它,如下所示:

    MyWonder

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...