如何在一个文件中定义多个相互依赖的 Perl 包
How to define multiple Perl packages in one file that are inter-dependent
我试过遵循其他一些示例,结果如下:
{
package Foo::Bar;
}
{
package Foo::Baz;
use Foo::Bar;
}
use Foo::Baz;
# some more code
# Fails with: Can't locate Foo/Bar.pm in @INC
我的真实示例是我想 bundle/concat https://github.com/TeX-Live/texlive-source/blob/trunk/texk/texlive/linked_scripts/texlive/fmtutil.pl along with its dependencies https://github.com/TeX-Live/installer/blob/master/tlpkg/TeXLive/TLConfig.pm and https://github.com/TeX-Live/installer/blob/master/tlpkg/TeXLive/TLUtils.pm 到一个文件中。
谢谢!
您没有 use
在同一个文件中定义的包。它们已经通过加载文件进行了编译和解析。示例:
{
package Foo::Bar;
sub bar { print "bar\n" }
}
{
package Foo::Baz;
sub baz {
Foo::Bar::bar();
print "baz\n"
}
}
use strict;
use warnings;
Foo::Baz::baz();
输出:
bar
baz
use
语句用于从 @INC
.
加载其他文件(Perl 模块)
问题是 Perl 不知道模块已经加载,因为您没有忠实地复制加载过程。具体来说,您没有修改 %INC
.
您还可能遇到在编译时未加载模块的问题。这可以使用 BEGIN
块来实现。
要内联模块,请将以下内容添加到脚本的开头:
BEGIN {
# Insert module here.
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
所以如果你有
# script.pl
use strict;
use Foo::Baz;
# ...
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
1;
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
1;
你最终会得到
BEGIN {
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
BEGIN {
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
# script.pl
use strict;
use Foo::Baz;
# ...
请注意,以上内容并非 100% 等同于内联模块。例如,相当于
use 5.012;
use open ":std", ":encoding(UTF-8)";
use Some::Module;
实际上是
# Non-lexical effects
BEGIN {
require 5.012;
binmode(STDIN, ":encoding(UTF-8)");
binmode(STDOUT, ":encoding(UTF-8)");
binmode(STDERR, ":encoding(UTF-8)");
}
BEGIN {
package Some::Module;
...
$INC{"Some/Module.pm"} = 1;
}
# Lexical effects
use 5.012;
use open ":encoding(UTF-8)";
要正确地内联模块,最好使用 @INC
挂钩。使用第一种方法中的文件,最终会得到
BEGIN {
my %modules = (
"Foo/Bar.pm" => <<'__EOI__',
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
1;
__EOI__
"Foo/Baz.pm" => <<'__EOI__',
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
1;
__EOI__
);
unshift @INC, sub {
my $module = $modules{$_[1]}
or return;
return $module;
};
}
# script.pl
use strict;
use Foo::Baz;
# ...
App::FatPacker 可用于以这种方式内联模块。
警告和错误中的行号将是原始文件的行号。添加到内联模块的 #line
指令将对此进行调整。
在正常设置中,use Module
调用有两个任务:
- 找到源文件
Module.pm
并像编译阶段 require
调用一样解析它,并且
- 调用
Module::import
函数
通过内联你的模块,你不需要做第一份工作。模块的功能和设置代码已在正确的命名空间中定义。
为第二步调用 import
可能是必要的,也可能不是必要的,或者可能需要多次,具体取决于可以导出的函数以及您希望在哪些命名空间中使用它们。
package Foo::Bar;
use parent 'Exporter';
@Foo::Bar::EXPORT_OK = qw(bar1 bar2);
sub bar1 { 47 }
sub bar2 { 73 }
package Foo::Baz;
@Foo::Baz::EXPORT_OK = "baz1";
use parent 'Exporter';
Foo::Bar->import("bar1");
sub baz1 { return bar1() + 42 }
package main;
Foo::Bar->import("bar2");
Foo::Baz->import("baz1");
sub main2 { return bar2() + baz1() + 19 }
我试过遵循其他一些示例,结果如下:
{
package Foo::Bar;
}
{
package Foo::Baz;
use Foo::Bar;
}
use Foo::Baz;
# some more code
# Fails with: Can't locate Foo/Bar.pm in @INC
我的真实示例是我想 bundle/concat https://github.com/TeX-Live/texlive-source/blob/trunk/texk/texlive/linked_scripts/texlive/fmtutil.pl along with its dependencies https://github.com/TeX-Live/installer/blob/master/tlpkg/TeXLive/TLConfig.pm and https://github.com/TeX-Live/installer/blob/master/tlpkg/TeXLive/TLUtils.pm 到一个文件中。
谢谢!
您没有 use
在同一个文件中定义的包。它们已经通过加载文件进行了编译和解析。示例:
{
package Foo::Bar;
sub bar { print "bar\n" }
}
{
package Foo::Baz;
sub baz {
Foo::Bar::bar();
print "baz\n"
}
}
use strict;
use warnings;
Foo::Baz::baz();
输出:
bar
baz
use
语句用于从 @INC
.
问题是 Perl 不知道模块已经加载,因为您没有忠实地复制加载过程。具体来说,您没有修改 %INC
.
您还可能遇到在编译时未加载模块的问题。这可以使用 BEGIN
块来实现。
要内联模块,请将以下内容添加到脚本的开头:
BEGIN {
# Insert module here.
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
所以如果你有
# script.pl
use strict;
use Foo::Baz;
# ...
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
1;
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
1;
你最终会得到
BEGIN {
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
BEGIN {
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
# script.pl
use strict;
use Foo::Baz;
# ...
请注意,以上内容并非 100% 等同于内联模块。例如,相当于
use 5.012;
use open ":std", ":encoding(UTF-8)";
use Some::Module;
实际上是
# Non-lexical effects
BEGIN {
require 5.012;
binmode(STDIN, ":encoding(UTF-8)");
binmode(STDOUT, ":encoding(UTF-8)");
binmode(STDERR, ":encoding(UTF-8)");
}
BEGIN {
package Some::Module;
...
$INC{"Some/Module.pm"} = 1;
}
# Lexical effects
use 5.012;
use open ":encoding(UTF-8)";
要正确地内联模块,最好使用 @INC
挂钩。使用第一种方法中的文件,最终会得到
BEGIN {
my %modules = (
"Foo/Bar.pm" => <<'__EOI__',
# Foo/Bar.pm
package Foo::Bar;
use strict;
# ...
1;
__EOI__
"Foo/Baz.pm" => <<'__EOI__',
# Foo/Baz.pm
package Foo::Baz;
use strict;
use Foo::Bar;
# ...
1;
__EOI__
);
unshift @INC, sub {
my $module = $modules{$_[1]}
or return;
return $module;
};
}
# script.pl
use strict;
use Foo::Baz;
# ...
App::FatPacker 可用于以这种方式内联模块。
警告和错误中的行号将是原始文件的行号。添加到内联模块的 #line
指令将对此进行调整。
在正常设置中,use Module
调用有两个任务:
- 找到源文件
Module.pm
并像编译阶段require
调用一样解析它,并且 - 调用
Module::import
函数
通过内联你的模块,你不需要做第一份工作。模块的功能和设置代码已在正确的命名空间中定义。
为第二步调用 import
可能是必要的,也可能不是必要的,或者可能需要多次,具体取决于可以导出的函数以及您希望在哪些命名空间中使用它们。
package Foo::Bar;
use parent 'Exporter';
@Foo::Bar::EXPORT_OK = qw(bar1 bar2);
sub bar1 { 47 }
sub bar2 { 73 }
package Foo::Baz;
@Foo::Baz::EXPORT_OK = "baz1";
use parent 'Exporter';
Foo::Bar->import("bar1");
sub baz1 { return bar1() + 42 }
package main;
Foo::Bar->import("bar2");
Foo::Baz->import("baz1");
sub main2 { return bar2() + baz1() + 19 }