substituting variables in BEGIN push #INC - perl

So instead of wanting to put
BEGIN {push #INC , '/folder1', '/folder2', '/folder3'}
to find my module.
I want to be able to put a variable instead such as this.
my $test = '/folder1';
BEGIN {push #INC , $test, '/folder2', '/folder3'}
No matter what I do it still wont find the module I want.

A BEGIN executes during parsing of the code, so it would be executed before a variable assignment outside of it occurs.
The solution is to do the assignment inside the BEGIN block:
my $test;
BEGIN {
$test = "/some/path";
}
use lib $test, "/path2", "path3";
The use lib … is preferable to unshift #INC, …

Instead of manually manipulating #INC, I suggest that you use lib:
use lib '/folder2', '/folder3';
Also, use and BEGIN blocks are executed as soon as possible, so if you want to include a variable in one, it must be initialized in a BEGIN block:
our $test;
BEGIN {
$test = "/some/path";
}
use lib $test, '/folder2', '/folder3';

The BEGIN statement runs before anything else runs -- even if the BEGIN statement appears later in the program. Variables you're using must also appear inside the BEGIN. Any variables declared with my will be out of scope once the BEGIN statement ends.
BEGIN {
my $test = '/folder1';
push #INC, $test, '/folder2', '/folder3';
}
I'm not sure what this is buying you. You still need to modify the program. You may want to use lib;:
use lib qw(/folder1);
use lib qw(/folder2 /folder3);
# use lib qw(/folder4); # Don't use

Related

Using lib pragma after explicitly setting #INC has no effect

use feature qw(say);
use strict;
use warnings;
BEGIN {
#INC = qw(dir1 dir2); # <-- lib pragma works fine if I omit this line
# #INC = (); <-- this is what I wanted to do
use lib 'dir3';
}
say for #INC;
Output:
dir1
dir2
Expected output:
dir3
dir1
dir2
A use statement is contained within an implicit BEGIN block, and
all Perl BEGIN blocks in a program are are executed as soon as they finish compilation
That means you have nested BEGIN blocks here, and because the inner one—the use lib 'dir3' statement—finishes compilation at the end of the statement, it is the first to be executed. It is followed by the closing brace of the explicit BEGIN block, which completes that block's compilation and it is only then that those statements are executed
Here is a sample program that uses only say statements, and replaces the use statement in the original with an explicit BEGIN block
use strict;
use warnings;
use feature 'say';
BEGIN {
say "outer BEGIN";
BEGIN {
say "inner BEGIN";
}
}
output
inner BEGIN
outer BEGIN
Your code works as if you had written it like this
use feature qw(say);
use strict;
use warnings;
use lib 'dir3';
BEGIN {
#INC = qw(dir1 dir2);
}
say for #INC;
So the use lib is executed first, adding dir3 to #INC, and then the explicit BEGIN block is executed and overwrites #INC altogether
If you want to empty #INC first and then add to it with use lib statements then you should write
BEGIN {
our #INC = ( );
}
use lib 'dir3';
Without the nesting, BEGIN blocks are executed in the order the appear in the program
From perldoc - lib
This is a small simple module which simplifies the manipulation of #INC at compile time.
When you insert say for #INC; after the opening brace and before the assignment, e.g.
BEGIN {
say for #INC;
#INC = qw(dir1 dir2); # <-- lib pragma works fine if I omit this line
# #INC = (); <-- this is what I wanted to do
use lib 'dir3';
}
You will see dir3 listed.
And perldoc - use states
Because use takes effect at compile time, it doesn't respect the ordinary flow control of the code being compiled.
dir3 is inserted into #INC before anything else is executed, and then #INC is assigned the new value (dir1, dir2). You see this new assigned value, when you look at #INC finally.

BEGIN block and variable declaration

Is it valid perl to set a variable in a BEGIN block, but declare the variable outside the BEGIN block?
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
my $var;
BEGIN{ $var = 10 }
say $var;
Yes, it's valid. In fact, you must do it that way, or $var would be local to the BEGIN block and not available in the rest of your program. To quote perlsub:
A my has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. ... Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example.
The compile-time effect is why you can access the variable in the BEGIN block. Be aware that any initialization on the my will take place after the BEGIN block is evaluated (and thus will overwrite any value the BEGIN might set.)
Yes, but you might want to be careful with this pattern, because something very similar will work differently than you might expect:
my $var = 5;
BEGIN { $var = 10 }
say $var; # 5

Initializing global variable on Perl module (with BEGIN block) include

I'm trying to initialize a global variable from a Perl module that has a BEGIN block, but I can't manage to get it work.
This is the Perl module
Package A::B;
our $var;
BEGIN{
$var ||= "/some/default/path";
#create/access files/folders in $var
}
This is my CGI script
use A::B;
$A::B::var = "/correct/path";
But #error returned because $var is not the correct path
The BEGIN block is being executed before the correct path is assigned to $var. Is there a way to work around this without having to remove code from the BEGIN block?
BEGIN { $A::B::var = "/correct/path" }
use A::B;
This answer is unsatisfying to me, but I can't think of any other way, given how your A::B is designed. Ideally a module doesn't depend on who is using it or how often, but this module can really only be used once.

How do you use a variable in lib Path?

I would like to use the $var variable in lib path.
my $var = "/home/usr/bibfile;"
use lib "$var/lib/";
However when I do this it throws an error.
I'd like to use use lib "$var/lib/";instead of use lib "/home/usr/bibfile/lib/";.
How can I assign a variable, so that it can be used in the setting of lib modules?
Variables work fine in use lib, well, just like they do in any string. However, since all use directives are executed in BEGIN block, your variable will be not yet initialized at the moment you run use, so you need to put initialization in BEGIN block too.
my $var;
BEGIN { $var = "/home/usr/bibfile"; }
use lib "$var/lib/";
use Data::Dumper;
print Dumper \#INC;
Gives:
$VAR1 = [
'/home/usr/bibfile/lib/',
# ... more ...
];
Not sure about what you're trying to accomplish, but seems like a task for FindBin::libs:
my $var;
BEGIN { $var = "/home/usr/bibfile" };
use FindBin::libs "Bin=$var", "base=lib";
You can't, because the use directive is evaluated at compile time, while other variables are evaluated at runtime.
If your lib is located somewhere relative to your original script, you can use the standard module FindBin:
# $Bin from FindBin is the directory of the original script
use FindBin;
use lib "$FindBin::Bin/path/to/bib";
use MyModule;

perlmod question

In the example in perlmod/Perl Modules there is a BEGIN block. I looked at some modules but none of these had a BEGIN block. Should I use such a BEGIN block when writing a module or is it dispensable?
You only need a BEGIN block if you need to execute some code at compile time versus run-time.
An example: Suppose you have a module Foo.pm in a non-standard library directory (like /tmp). You know you can have perl find the module by modifying #INC to include /tmp. However, this will not work:
unshift(#INC, '/tmp');
use Foo; # perl reports Foo.pm not found
The problem is that the use statement is executed at compile time whereas the unshift statement is executed at run time, so when perl looks for Foo.pm, the include path hasn't been modified (yet).
The right way to accomplish this is:
BEGIN { unshift(#INC, '/tmp') };
use Foo;
Now the unshift statement is executed at compile-time and before the use Foo statement.
The vast majority of scripts will not require BEGIN blocks. A lot of what you need in BEGIN blocks can be obtained through use-ing other modules. For instance, in this case we could make sure /tmp is in #INC by using the lib.pm module:
use lib '/tmp';
use Foo;
A BEGIN block in a module is entirely dispensable. You only use it if there is something that must be done by your module when it is loaded, before it is used. There are seldom reasons to do much at that point, so there are seldom reasons to use a BEGIN block.