如何为 Raku CLI 使用包装器脚本

How to use a wrapper script for a 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, Int  :$length = 24, Bool :$verbose) { #`(body omitted)}

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

然而,none 这利用了 Raku 的编译。因此,在 MAIN 中执行更多处理的 CLI 应用程序可能会变得有点慢(即使生成 --help 输出)。所以,下一步是让应用程序被预编译,但我不太确定如何做。

有 Zef

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

#!/usr/bin/env perl6
sub MAIN(:$name, :$auth, :$ver, *@, *%) {
    CompUnit::RepositoryRegistry.run-script("frobnicate", :$name, :$auth, :$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 或类似的东西吗?)以某种方式让他们将两者都安装到他们 [= 的目录中28=]?

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

So I'm guessing Zef adds it?

CompUnit::Repository::Installation adds it

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

Is there some way to use the run-script method shown above without Zef?

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

Assuming I do need to have my users download two files, where should I have them install the files?

好吧,recommended/idiomatic 方法是使用核心 raku 功能来安装东西(即使用 zef)。否则你应该放一些代码的地方要么 a) 无关紧要要么 b) 将 mos 完全依赖于你的环境,什么是你的 os 惯用的,等等

Does it need to be copied into their Raku module search path (and, if so, what command would show them what that path is)

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

也是如此

Or can I modify the frobnicate wrapper in some way (maybe by changing raku to raku -Ilib or something like that?) in a way that would let them install both to directory on their PATH?

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

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

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

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

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

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

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

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

    然后当zef安装发行版时,MyWonder将在命令行上工作(假设zef本身在命令行上工作,这意味着PATH包含zef 的目录)。

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

    • 将以下内容放入MyWonder文件(无需扩展名)
    use v6.d;
    use MyWonderLife;
    
    • 将所有功能放入 MyWonderLife.rakumod
    • 确保MyWonderLife.rakumod
    • 中至少有一个sub MAIN
    • META6.json 文件中指定 MyWonderLife(这样可以非常灵活地选择将实际 MyWonderLife.rakummod 文件放在哪个目录下;它不必是 lib/)
    • 为分发创建一个简单的测试 t/basic.rakutest,只有测试 use-ok 'MyWonderLife;

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

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

    我发现在安装模块时进行大量测试是一件很痛苦的事情,所以我开始将大部分开发和维护测试转移到 xt/ 并且只在 t/.

    最后,有了这个秘诀(假设您在 META6.json 文件中为发行版指定了名称 MyWonderLife,MyWonder 的安装说明,假设可以调用程序而不用参数很简单:

    使用zef安装MyWonderLife,例如

    zef install MyWonderLife
    

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

    MyWonder