I've tried following some other examples and have something like:
{
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
My real-world example is I'd like to 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 into a single file.
Thank you!
CodePudding user response:
The problem is that Perl doesn't know the module is already loaded because you haven't faithfully replicated the loading process. Specifically, you didn't modify %INC
.
You could also have problems from not loading the module at compile-time. This could be achieved using a BEGIN
block.
To inline a module, add the following to the start of your script:
BEGIN {
# Insert module here.
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . ".pm" } = 1;
}
So if you had
# 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;
You'd end up with
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;
# ...
Note the above isn't 100% equivalent to inlining the modules. For example, the equivalent of
use 5.012;
use open ":std", ":encoding(UTF-8)";
use Some::Module;
would actually be
# 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)";
To properly inline a module, it would be better to use an @INC
hook. Using the files from the first approach, one would end up with
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 { $modules{$_[1]} };
}
# script.pl
use strict;
use Foo::Baz;
# ...
App::FatPacker can be used to inline modules this way.
The line numbers will be the line numbers of the original file. a #line
directive added to the inlined module would adjust that.
CodePudding user response:
You do not use
packages defined in the same file. They are already compiled and parsed by loading the file. Example:
{
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();
Output:
bar
baz
The use
statement is used to load other files (Perl modules) from @INC
.
CodePudding user response:
In normal settings, the use Module
call has two jobs:
- Find the source file
Module.pm
and parse it like a compile-phaserequire
call, and - Call the
Module::import
function
By inlining your modules, you don't need to do the first job. The functions and set up code for the module are already defined in the correct namespace.
Calling import
for the second step may or may not be necessary, or may be necessary several times, depending on what functions can be exported and what namespaces you want to use them in.
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 }