How can I use an external lib in a BEGIN block - perl

I want to use an external library in the BEGIN block of a Perl script.
The first test which I did was to check whether my #INC is getting popullated if I push some values:
use strict;
use warnings;
BEGIN {
push #INC, "d:/external_pm/";
use Data::Dumper;
print Dumper #INC;
}
Which works as expected and shows:
$VAR1 = 'D:/perl/5163/site/lib';
$VAR2 = 'D:/perl/5163/lib';
$VAR3 = '.'; # I am not sure about this one?!
$VAR4 = 'd:/external_pm/';
Now I want to import a module right after the push:
use strict;
use warnings;
BEGIN {
push #INC, "d:/external_pm/";
use Data::Dumper;
print Dumper #INC;
use ExtScript;
}
The error which follows is showing me that #INC was not updated:
Can't locate ExtScript.pm in #INC (#INC contains: D:/perl/5163/site/lib
D:/perl/5163/lib .) at file.pl line 9.
BEGIN failed--compilation aborted at file.pl line 9.
Why the #INC it's not updating?
I can't import a module in the BEGIN block? Or is a missusage of Perl?

use statements are executed at compile time (specifically, during the BEGIN phase), whereas normal code is run later. Let's look at this simplified snippet:
BEGIN {
push #INC, "some/dir";
use Example;
}
If we spell out all phases explicitly, that would be equivalent to:
BEGIN {
push #INC, "some/dir";
BEGIN { require Example; Example->import() }
}
So the Example module will be imported before the push runs.
There are a number of ways to address this.
The simplest way is to put only the #INC manipulation into a BEGIN block, and import the module outside:
BEGIN { push #INC, "some/dir" }
use Example;
A better solution is to use the lib pragma to handle #INC:
use lib "some/dir";
use Example;
However, there is a major difference: use lib puts additional directories at the beginning of the module search path, so you may accidentally override other modules. push #INC only adds directories to the end, as a fallback if a module wasn't found in other locations.

use ExtScript; is executed before push #INC. Either
move use ExtScript; out of the BEGIN block
or change it to require/import
or use the -I command-line option or PERL5LIB env variable.

Related

In Perl, can't locate packgeName.pm in #INC error

This is a module math.pm with 2 basic functions add and multiply:
package Math;
use strict;
use warnings;
use Exporter qw(import);
our #EXPORT_OK = qw(add multiply);
sub add {
my ($x, $y) = #_;
return $x + $y;
}
sub multiply {
my ($x, $y) = #_;
return $x * $y;
}
1;
This is script script.pl that call the add function:
#!/usr/bin/perl
use strict;
use warnings;
use Math qw(add);
print add(19, 23);
It gives an error:
can't locate math.pm in #INC <#INC contain: C:/perl/site/lib C:/perl/lib .> at C:\programs\script.pl line 5.
BEGIN failed--compilation aborted at C:\programs\script.pl line 5.
How to solve this problem?
use lib
Adding a use lib statement to the script will add the directory to #INC for that specific script. Regardless who and in what environment runs it.
You just have to make sure to have the use lib statement before trying to load the module:
use lib '/path/to/module';
use Math qw(add);
For more details to set #INC look at this:
How do I include a Perl module that's in a different directory
Add the following to script.pl before use Math ...;:
use FindBin qw( $RealBin );
use lib $RealBin;
If script.pl and math.pm aren't in the same directory, adjust accordingly.
Also, you might have problems if the file is named math.pm and you use use Math; and package Math;. It would be best to rename the file so the spelling is consistent.
ren math.pm Math.pm

A Perl script and a Perl module are using `use lib` and referencing the same module

I have a Perl script that is using two modules. The first module also uses the second module.
If the first module does use lib 'dir1', and the script does use lib 'dir2':
module1.pm
use lib "dir1";
use module2;
script.pl
use lib "dir2";
use module1;
use module2;
which directory is being used by the first module? Does the script's use lib, 'dir2' override the use lib, 'dir1' in the first module, or do they have different scope?
I did my own testing, and printed the #INC inside module1.pm and script.pl, the result was the dir1 was at the top of #INC.
The lib pragma just does an unshift of the path onto the global (and package-independent) array #INC. Nothing is ever removed from #INC unless it is done explicitly.
The first time it is encountered is on the first line of the script.pl, so thereafter #INC will contain dir2.
The next thing that happens is that script.pl compiles and runs module1.pm. (It will look for it in dir2 first, and then in the directories that were originally in #INC.) The first line of that file contains another use lib, which will unshift #INC, 'dir1'.
Thereafter #INC stays the same. Any further use or require will look in dir1 first (because it was shifted last) then dir2, and then in the rest of #INC.
You obviously know that lib works on the #INC array. However, you appear to be thinking of it as an array of a single value that is replaced by subsequent usage.
Borodin's already explained the mechanisms well, and the docs for lib do an even better job. However, perhaps a little example script will help:
use strict;
use warnings;
use Data::Dump qw(dump);
# Set #INC to minimum required for lib
BEGIN {
require lib;
#INC = $INC{'lib.pm'} =~ s/lib.pm//r;
print "\#INC = " . dump(#INC) . "\n\n";
}
# Try adding two directories
use lib 'foo';
BEGIN {
print "use lib 'foo';\n";
print "# \#INC is " . dump(#INC) . "\n\n";
}
use lib 'bar';
BEGIN {
print "use lib 'bar';\n";
print "# \#INC is " . dump(#INC) . "\n\n";
}
# Remove a directory
no lib 'foo';
BEGIN {
print "no lib 'foo';\n";
print "# \#INC is " . dump(#INC) . "\n";
}
Outputs:
#INC = "C:/strawberry/perl/lib/"
use lib 'foo';
# #INC is ("foo", "C:/strawberry/perl/lib/")
use lib 'bar';
# #INC is ("bar", "foo", "C:/strawberry/perl/lib/")
no lib 'foo';
# #INC is ("bar", "C:/strawberry/perl/lib/")
See the perl doc on use lib: http://perldoc.perl.org/lib.html
When script.pl runs, it will prepend "dir2" to #INC. It will then look for module1 in the directories listed in #INC starting with "dir2".
When it is loading module1, it will prepend "dir1" to #INC and then look for and load module2 in the directories now listed in #INC, which starts with dir1, then dir2 and then the remaining directories in #INC. Once it has loaded module2, it will finish loading module1.
The use module2 line in script.pl will be ignored at this point.

BEGIN blocks in Perl

Searching the Internet, I get that the BEGIN block will get evaluated and executed in the compile phase. But can the #INC or other variables be inherited?
Below is what I wrote for testing. The directory structure is as:
|-- alexpackages
| |-- alex1.pm
| `-- alex2.pm
|-- foo.pl
`-- main.pl
For each file:
cat alexpackages/alex1.pm
package alex1;
sub foo()
{
print "this is alex1::foo\n";
}
1;
cat alexpackages/alex2.pm
package alex2;
sub foo2()
{
print "this is is alex2::foo2\n";
}
1;
cat foo.pl
alex1::foo();
cat main.pl
BEGIN
{
push(#INC, '~/programs/perl/alexpackages');
}
use strict;
use warnings;
use alex1;
use alex2;
#alex1::foo(); # 1. This works good
system("perl foo.pl"); # 2. This fails
Just as what my program tells that the #INC does not work for a new system call. It seems to me that system call will not inherit the system environment. Am I right?
And can how I make the environment variables to the following system call?
Processes started with system will inherit the environment variables from the calling process, but #INC is just a global Perl variable, not a system environment variable. It is not visible outside the Perl program.
A couple of notes on your code
Package names, being globals, should be capitalised, so your packages should be Alex1 and Alex2, in files alexpackages/Alex1.pm and alexpackages/Alex2.pm
It is best to use the lib pragma to manipulate #INC, so
use lib '~/programs/perl/alexpackages'
is best. And a use statement creates an implicit BEGIN block, so that is also unnecessary.
It is wrong to use prototypes on Perl subroutines, so sub foo() should be just sub foo
You may prefer to use Exporter to copy the symbols of a package into the calling code. That way you don't have to fully-qualify your subroutine name when you call it, like foo() instead of Alex1::foo()
The code would look like this
main.pl
use strict;
use warnings;
use lib '~/programs/perl/alexpackages';
use Alex1;
foo();
~/programs/perl/alexpackages/Alex1.pm
package Alex1;
use strict;
use warnings;
use base 'Exporter';
our #EXPORT = qw/ foo /;
sub foo {
print "This is Alex1::foo\n";
}
1;
There are many ways to specify library search directories both from within a Perl script or the command line. Here are two things you could do to make another directory available to the Perl script you invoke with system:
$ENV{PERL5LIB} = "~/programs/perl/alexpackages";
system("perl foo.pl");
system("perl -I~/programs/perl/alexpackages foo.pl");
Both PERL5LIB and the -I switch are documented in perlrun.

How to change #INC lib contents by using no lib command in PERL

if (#arr =~ /env1/) #checking enviroment
{
# Want to flush the #INC contents using below no lib
# --> Command before the perl get the libraries of my choice at the time of compilation
no lib "//First/lib/"; #flush INC
no lib "//Second/lib/"; #flush INC
print log1 "INC before current- #INC\n";
#set library of my choice with either syntax
BEGIN{ unshift #INC, "//Third/lib/" };
# or
use lib "//third/lib/";
print log1 "INC after- #INC\n";
print log2 "INC after- %INC\n";
print log3 map {"$_ => $INC{$_}\n"} keys %INC;
use DirHandle;
use File::Find;
use File::Copy;
print map {"$_ => $INC{$_}\n"} keys %INC;
}
I want to know the above thing can be achieved in a simpler way. Do I need to include any Perl modules to use the (no lib and use lib commands). This is a requirement due to script running in multiple enviroment and when one of the enviroments is down the script stops running. I want to achieve this by passing the libraries of my choice to be used by perl.
Suggestions are welcome.
Edit:
Here's the actual code.
if (#temp[arr_index]=~ /env1/) #enviroment 1
{
BEGIN{ unshift #INC, "//server1/lib/" }; #server 1 for same app lower enviroment 1
BEGIN{ unshift #INC, "//server2/lib/" }; #server 2 for same app lower enviroment 1
use File::Copy;
}
elsif (#temp[arr_index]=~ /env2/) #enviroment 2
{
BEGIN{ unshift #INC, "//server1/lib/" }; #server 1 for same app lower enviroment 2
BEGIN{ unshift #INC, "//server2/lib/" }; #server 2 for same app lower enviroment 2
use File::Copy;
}
elsif (#temp[arr_index]=~ /env3/) #enviroment 3
{
BEGIN{ unshift #INC, "//server1/lib/" }; #server 1 for same app lower enviroment 3
BEGIN{ unshift #INC, "//server2/lib/" }; #server 2 for same app lower enviroment 3
use File::Copy;
}
#INC - Having #INC the values same for all enviroments which is causing issue. Hope the above adds some clarity to your understanding . THANKS.
I would suggest just pushing all of them into #INC in the order of your preferred environments. Perl will look at the contents of #INC. Perlvar says about it:
The array #INC contains the list of places that the do EXPR , require,
or use constructs look for their library files.
That means it will look at the list of paths from left to right, as described here. Thus, you can just push your library paths into #INC one after another:
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
use lib 'first/lib', 'second/lib', 'third/lib';
}
If you now go and require Some::Module, Perl will look in first/lib first. If it cannot find it there, maybe because the path doesn't exist in your environment (it won't complain about that), Perl will proceed to look in second/lib and third/lib one after the other. You don't have to do anything else.
--
On another note, you have mixed compile time and run time a lot in your above code. The three use parts and the BEGIN block will be called at compile time regardless of the if construct they are in. Everything else, including the no lib calls will only be called at run time if the match is true. See perlmod for a more detailed explanation of how this works.

Why doesn't my Perl script use my module?

module.pm
package module;
use 5.012;
use warnings;
sub Parse
{
return 1;
}
1;
script.pl
#!/usr/bin/perl -w
use 5.012;
use warnings;
use lib 'C:/';
use module;
print Parse("value");
Stdout
Undefined subroutine &main::Parse
You need either to write:
print module::Parse("value");
or to change the module package to export the name Parse.
See http://perldoc.perl.org/perlmod.html#Perl-Modules for guidance in exporting symbols from your module.
(By the way, you should really name your module Module rather than module. Lowercase module-names are used for Perl built-in features like use warnings and use strict.)
Several things:
First, use Local as your module prefix. That way, if you just happen to have a module with the same name in your Perl installation, it will use yours. Call it "Local::Module". Then, create a Local directory, and name your module Module.pm.
The other thing you have to understand is that you define your module in another namespace. By default, everything is in the main namespace until you use the package statement. That creates another namespace that your package uses. This way, if your package has a function foo, and you've defined a function foo in your main program, they won't collide.
Thus, you have two choices: One (the preferred now) is to simply call your subroutine with the full package name prepended to it. The second is to export your subroutine names to your main program. This can cause problems with duplicate names, but you don't have to keep typing in the package name every time you call your subroutine.
Without Exporting the name
Local/Module.pm
# /usr/bin/env perl
# Local/Module.pm
package Local::Module;
use strict;
use warnings;
sub Parse {
my $value = shift; #Might as well get it.
print "I got a value of $value\n";
return $value;
}
1; #Need this or the module won't load
program.pl
# /usr/bin/env perl
# program.pl
use strict;
use warnings;
use Local::Module;
Local::Module::Parse("Foo");
With export:
Local/Module.pm
# /usr/bin/env perl
# Local/Module.pm
package Local::Module;
use strict;
use warnings;
use Exporter qw(import);
our #EXPORT_OK(Parse); #Allows you to export this name into your main program
sub Parse {
my $value = shift; #Might as well get it.
print "I got a value of $value\n";
return $value;
}
1; #Need this or the module won't load
program.pl
# /usr/bin/env perl
# program.pl
use strict;
use warnings;
use Local::Module qw(Parse);
Parse("Foo");