【问题标题】:How to distribute native perl scripts without custom module overhead?如何在没有自定义模块开销的情况下分发本机 perl 脚本?
【发布时间】:2009-11-10 18:59:59
【问题描述】:

如何在不强制用户了解脚本运行所需的自定义(非 CPAN)模块的情况下分发本机(非“已编译/perl2exe/...”)Perl 脚本?

问题是用户不可避免地会将脚本复制到系统上的其他地方并将脚本从其本地环境中取出,然后它就无法再找到它需要运行的模块。

我有时只是将模块复制到实际脚本中,但我更喜欢更简洁的解决方案。

更新:我最好澄清一下。我分发了一堆恰好在后端使用类似模块的脚本。用户了解如何运行 Perl 脚本,但与其依赖于告诉他们“不,不要移动脚本”,我更愿意简单地允许他们移动文件。阻力最小的路径。

【问题讨论】:

  • 模块是特定脚本私有的还是由多个脚本共享的?前一种情况通常通过将模块放在位于 *.pl 文件旁边的 lib 文件夹中来实现。后者意味着将模块安装到 site/lib 或 PERL5LIB 中的其他位置。

标签: perl distribution bundle


【解决方案1】:

正确的方法是告诉他们“不要那样做!”我希望他们不会期望移动 exe 文件并让程序继续工作。这也不例外。

也就是说,有几种选择。一种是用知道真实脚本完整路径的包装器(例如 pl2bat)替换脚本。另一个是使用PAR,但这需要安装 PAR 和/或 parl(来自 PAR::Packer)。

【讨论】:

  • +1 没有必要对此投反对票。 OP的要求是矛盾的。
  • 我非常感谢您对为什么有人认为它没有帮助的评论。
  • 我对你的答案的两个部分都有问题。来自一般的 IT 背景,我发现期望已知的用户行为改变以适应模糊的技术要求是不切实际的。此外,我认为没有正式安装程序的 exe 文件 /should/ 可以在系统上的任何位置工作。如果程序具有外部依赖项,则应正确安装它们,因此文件系统中的位置无关紧要。
  • 迈克尔是对的。您可以只使用 PAR 来生成可重定位的二进制文件。虽然 OP 要求提供不同的解决方案,但他没有提供原因。
  • “使用 PAR”是指将整个脚本(模块和所有)打包到单个 *.par 文件中。这是基于模块是特定于应用程序的假设,他只是试图阻止它们从脚本中分离出来。如果它们应该是可共享的,那就另当别论了。
【解决方案2】:

如果您为客户准备的脚本需要“自定义”模块,只需打包您的模块,就像您尝试将它们上传到 cpan 一样。然后将包交给客户端,他可以使用 cpan 实用程序安装脚本和模块。

【讨论】:

  • 嗯……也许吧。我将问题理解为“最终用户在安装后移动 *.pl”(可能是在错误假设它是独立的)而不是如何安装。
  • 我将其解读为“如何安装与脚本一起使用的模块?”。事实上,一个包也可以安装脚本本身只是一个额外的好处,恕我直言。
  • 我怀疑原始的“安装”包含“解压缩此文件”,而用户没有意识到文件之间的依赖关系。如果安装涉及更多——例如通过调用 cpan 或运行安装脚本——那么简单移动 .pl 文件的诱惑就会消失。是否需要并不重要;复杂安装的错觉足以将文件保留在原处。
  • 当然,如果你这样做,诱惑就会减少。但是,如果你正确地安装了你的模块,你也可以毫无问题地真正移动这些东西。
【解决方案3】:

随脚本一起分发安装程序。安装程序需要以 root 权限运行,并将自定义模块放入标准系统位置(/usr/local/lib/perl5/site_perl 或其他)。

我没有尝试过,但Module::Install 在这方面看起来很有帮助。它被描述为:

独立的、可扩展的 Perl 模块安装程序

【讨论】:

  • 将特定于应用程序的模块安装到系统范围的位置是冒文件/命名空间冲突风险的好方法。
  • 我想每件事都需要权衡取舍,但是在 Perl 模块的情况下,稍加考虑可以将命名空间冲突的风险降低到几乎为零。作者可以简单地创建一个基于他的姓名或公司名称的顶级命名空间,并且很可能是唯一的。
  • Module::Installer 并没有真正做任何您无法从 Module::Build 或 ExtUtils::Makemaker 获得的事情。
【解决方案4】:

作为“将您的模块全部放在一个地方并让您的应用程序意识到它”的一种变体,它甚至可以跨多台计算机和网络工作,也许您应该分别查看PAR::RepositoryPAR::Repository::Client。您只需提供一个二进制客户端可执行文件,该可执行文件连接到存储库(通过文件系统或 https)并执行用户请求的存储库提供的任意数量的程序(使用任意一组模块)。

如果有很多用户,这也有利于维护:只需更新存储库提供的软件,用户将在下次启动程序时开始使用更新后的系统代码。

【讨论】:

  • 这样的事情不需要他们在 perl 安装中安装这些模块吗?还是完全独立的东西?
  • PAR::Packer 生成完全独立的二进制可执行文件。但是,它确实将所有依赖项与应用程序打包在一起。因此,如果您希望在多个应用程序之间共享代码,这可能是不可取的。 (PAR 提供了多种解决此问题的方法,但我的评论长度已用完。)在存储库场景中,您只需发布一个 loader.exe,它可以根据需要从存储库中获取应用程序和依赖项。另见:steffen-mueller.net/talks/appdeployment
【解决方案5】:

如果您可以使用NeXTSTEP style application 捆绑包,那就太好了。由于您可能不是为使用捆绑包的平台进行开发,因此您必须凑合着做。

将所有支持文件放在已知位置,并将可执行文件指向这些文件以访问设置和库。最简单的方法是使用简单的安装程序。

例如,使用名为foo 的应用程序,将所有需要的文件放入/opt/xlyd_apps/foo,将库放入/opt/xlyd_apps/foo/lib,将配置放入/opt/xlyd_apps/foo/etc,等等。将可执行文件放入/opt/xlyd_apps/foo/bin

重要的是要确保可执行文件知道在/opt/xlyd_apps/foo 中查找其所有优点,因此如果客户/客户将foo 脚本移动到新位置,安装仍然有效。

因此,虽然您不能使整个事物自包含和可重定位,但您已经使实际的调用脚本可重定位。

【讨论】:

  • 这只是硬编码应用程序中的库路径。这似乎不是一个很好的方法除非它是平台的一个特性。也许添加一个间接步骤会使事情变得更理智。将您的自定义模块放入某个 /opt... 文件夹,然后将一个超级简单的模块安装到系统库中,该库知道自定义模块的路径。这样一来,您就不会在多个地方硬编码路径。
【解决方案6】:

我实际上已经想出了自己的解决方案,我有点好奇它会有什么样的反应。

我编写了一个脚本,它读取 perl 脚本并查找“use/require”语句。找到它们后,它会检查模块是否是标准库的一部分(查看 /perl5/\d+.\d+[\d.]+/ 的模块路径),然后以两种不同的方式重写 use/require 行,具体取决于用法。

如果找到 require

{
    ... inline the entire module here...
}

如果发现使用

BEGIN {
    ... inline the entire module here...
}

如果 useimports,请立即跟上:

BEGIN { import Module ...imports seen... }

我知道这适用于使用 XS 的模块,但我对此很好。大多数情况下,我只需要支持纯 perl 模块。

【讨论】:

  • 看一下 Module::ScanDeps 的依赖解析和 Module::CoreList 检查它是否带有默认库。
  • 除了它不使用 CPAN 模块来执行此操作之外...为什么此过程不好?答案的核心是将模块文本内联到脚本中。你没有给我一个不好的理由。
  • @xyld:如果它有效,那很好。但是在很多情况下它不起作用(当然包括 XS 模块)。虽然我不记得当我沿着这条路线走时让我绊倒的确切模块,但相信我,这不是一个强大的解决方案。那时,我实际上编写了一些中等复杂的代码来自动解决各种问题。从来没有完成它,并按照我所知道的工作良好的方式进行。当我遇到我的尝试时,我会告诉你的。
猜你喜欢
  • 2012-11-24
  • 2017-02-15
  • 2017-04-03
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
相关资源
最近更新 更多