Perl in Intellij IDEA: Global symbol "%Config" requires explicit package name (did you forget to declare "my %Config"?) lib.pm - perl

I'm developing a large perl module which works like a charm when running from Terminal. When running i with Intellij IDEA CE, the following error pops up. This happens in all major versions of the software.
My programm starts:
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use feature qw (say);
use Getopt::Long;
use lib 'lib';
die('this is a test');
...
Perls own lib.pm starts like this
package lib;
# THIS FILE IS AUTOMATICALLY GENERATED FROM lib_pm.PL.
# ANY CHANGES TO THIS FILE WILL BE OVERWRITTEN BY THE NEXT PERL BUILD.
use Config;
use strict;
my $archname = $Config{archname};
my $version = $Config{version};
my #inc_version_list = reverse split / /, $Config{inc_version_list};
our #ORIG_INC = #INC; # take a handy copy of 'original' value
our $VERSION = '0.65';
...
In Intellij IDEA this leads to
/usr/bin/perl -I/home/user/git/mytool/lib -I/home/user/git/mytool/lib/Download /home/user/git/mytool/download.pl Digi20
Global symbol "%Config" requires explicit package name (did you forget to declare "my %Config"?) at /usr/lib/x86_64-linux-gnu/perl-base/lib.pm line 10.
Global symbol "%Config" requires explicit package name (did you forget to declare "my %Config"?) at /usr/lib/x86_64-linux-gnu/perl-base/lib.pm line 11.
Global symbol "%Config" requires explicit package name (did you forget to declare "my %Config"?) at /usr/lib/x86_64-linux-gnu/perl-base/lib.pm line 12.
Compilation failed in require at /home/user/git/mytool/download.pl line 10.
I don't know where these -I params to the perl executable are configured. In the run dialog, i configured no params for perl.
Ubuntu 22.04 LTA + Perl 5.34. On my home office machine everything works fine, too. But on the office machine not. Syncing IDE settings home > office does not help.
Found another user having a similar issue on Eclipse but the error comes from another module. My Config module is already named Download::Config.

There's a module call Config that comes with Perl. It exports a hash named %Config by default.
The error is due to %Config not being exported by by use Config;.
I'm guessing a different module named Config is being picked up by use Config;. You can verify this using BEGIN { print "$INC{'Config.pm'}\n" } after the use Config;.
You should name your module something else.
That said, I suspect you don't actually have a module named Config. I suspect you have a module named Download::Config (which is perfectly fine), but /home/user/git/mytool/lib/Download is being incorrectly added to #INC.

I found the solution:
Intellij IDEA adds the configured library destinations as -I param to the perl call. Mind the purple marker here in the picture. The Download folder was purple, too. That caused the error.
There is a similar setting in the project structure settings but this does not cause -I parameters being added.

Related

Perl can't find module listed in same directory as script

I need to keep any custom modules in the same-ish directory as my script.
I keep getting this error suggesting that Perl can't "see" the module I'm declaring
Global symbol "$employment_type" requires explicit package name at early_enrollments_by_term.pl line 260.
Execution of early_enrollments_by_term.pl aborted due to compilation errors.
My module is Hash.pm and is in directory /home/pgb2/canvas/canvas-sis-feeds/scripts/lib/Custom
Hash.pm
package Custom::Hash;
My script is early_enrollments_by_term.pl and lives in /home/pgb2/canvas/canvas-sis-feeds/scripts/
early_enrollments_by_term.pl
#!/usr/bin/perl
use strict;
use DBI;
use lib qw(/home/pgb2/canvas/canvas-sis-feeds/scripts/lib);
use Custom::Hash;
my $enrollment_type = $Custom::Hash::enrollment_hash{$role}; # line 259
print "\nenrollment_type: $employment_type\n"; # line 260
Would anyone be able to tell why it's not finding the module?
Although you have shown very little of your code,
the problem would presumably be that you have declared $enrollment_type and tried to use $employment_type, which has not been declared
Had perl been unable to find the module, it is the use Custom::Hash statement that would throw the error

Check and report Perl module missing

Is there any way to report the missing modules used in the Perl file beforehand instead of getting as an error.
I have something like use Digest::MD5, use File::DosGlob modules in my Perl program. Whenever the users run the script they are getting an error if there is no specific module installed in their system. They could not understand the default error message given by #INC. So I would like to clearly tell them that these modules need to be installed to run the script.
You could build your own verification by using a BEGIN block. Those are run at compile time, just like use is. Keep in mind that use Foo is essentially nothing else as this:
BEGIN {
require Foo;
Foo->import;
}
The following code will replace all use statements with a single BEGIN and place them inside an eval. That's essentially like a try/catch mechanism.
We need the string eval (which is considered evil around here) because require only converts from package names with colons :: to paths if the argument is a bareword. But because we have the name in $module, it's a string, so we need to place it into an eval according to require's docs.
If that string eval fails, we die. That's caught by the outer eval block and $# is set. We can then check if it contains our module name, in which case we naively assume the failure was because that module is not installed. This check could be a bit more elaborate.
We keep track of any failures in $fails, and if there were any, we stop.
#!/usr/bin/perl
use strict;
use warnings;
# all our use statements go here
BEGIN {
my $fails;
foreach my $module ( qw/Digest::MD5 File::DosGlob ASDF/ ) {
eval {
eval "require $module" or die; # because $module is not a bareword
$module->import;
};
if ($# && $# =~ /$module/) {
warn "You need to install the $module module";
$fails++;
}
}
exit if $fails;
}
# ...
Above I included ASDF, which I don't have, so when run it will say
You need to install the ASDF module at /home/code/scratch.pl line 1335.
You might want to make that message a bit more verbose. If your users are not able to understand the default error message that Perl gives when it cannot find a module, it might be wise to include a guide on how to install stuff right there.
Note that both modules you listed have been included with Perl for a while (read: since March 2002). So why would you want to do this for those modules?
$ corelist Digest::MD5
Data for 2014-09-14
Digest::MD5 was first released with perl v5.7.3
$ corelist File::DosGlob
Data for 2014-09-14
File::DosGlob was first released with perl 5.00405
A better way would be ship your program as a distribution that can be installed, and include a Makefile or a cpanfile or something similar that lists dependencies. There is a guide in perlnewmod on how to start a new module. You'd not want to upload to CPAN obviously, but the basics are the same.
With this, your users would get all dependencies installed automatically.
You could use Devel::Modlist, it will list all the required module for your program.
perl -d:Modlist test.pl
There's another module Module::ScanDeps which comes with a utility scandeps.pl which you can use on your script as:
scandeps.pl test.pl
Note that sanity checking your Perl code using perl -c is dangerous, so use it carefully.
Your question isn't really clear about what "beforehand" means. To check if a Perl program's syntax is correct and directly included modules are resolvable, use
perl -c <perl-program.pl>
This checks the syntax of your file and ensures that any modules used by your code exist. However, it does not transitively check the entire dependency tree, only those mentioned in perl-program.pl.

Eclipse: Perl EPIC debug & release mode execution mismatch

I am noticing an execution mismatch of Perl in Eclipse EPIC plugin.
Specifications:
Perl version: (v5.12.4) built for MSWin32-x86-multi-thread
Eclipse version: Indigo Service Release 2
EPIC version: 0.6.53
Consider the files & source codes below: (all source files are in same directory)
sample.pl
use package1;
require "package1.pm";
require "package2.pm";
sampleFunction();
require "packagetest.pm";
packagetest::callSampleFunction();
package1.pm
package package1;
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(
sampleFunction
);
sub sampleFunction {
print "from package1.pm \n";
}
package2.pm
package package1; # declare the same package 'package1'
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(
sampleFunction
);
sub sampleFunction {
print "from package2.pm \n";
}
packagetest.pm
package packagetest;
use package1;
sub callSampleFunction {
sampleFunction();
}
1;
When I execute the sample.pl file in Eclipse EPIC, I am getting two different console outputs.
Debug mode output:
from package2.pm
from package2.pm
Run mode output:
Subroutine sampleFunction redefined at D:/My.Soft.Dev/Perl_XS/package2.pm line 11.
from package1.pm
from package2.pm
I have the below questions:
1) Why am I getting two different outputs here? Is it not a bug?
2) Which of the outputs shown is a "valid output"?
3) Can someone explain why is the particular output valid?
I tried to derive the reasons from my Perl knowledge. But I couldn't. So, I understood, I have to get to know more about Perl:))
UPDATE: I have created a bug report: https://sourceforge.net/p/e-p-i-c/bugs/669/
Looks like (1) is a bug in Perl debugger of 5.12.4 version
Regarding questions #2 and #3:
Your release mode output is the valid one:
use package1 finds, loads, compiles, and executes package1.pm. This defines package1::sampleFunction, which is exported into the main namespace. Exporting is effectively done by reference, and not by name, so main::sampleFunction points to the same function which package1::sampleFunction currently refers to.
require "package1.pm does nothing because that package has already been executed.
require "package2.pm finds, loads, compiles and executes package2.pm. This redefines package1::sampleFunction, which will warn if and only if you have warnings activated – either lexically through use warnings (do this) or globally with the -w switch (don't do this).
sampleFunction() executes main::SampleFunction, which still points to the original subroutine.
from package1.pm
require "packagetest.pm" finds, loads, compiles and executes packagetest.pm. Here in turn:
use package1 will export package1::sampleFunction (which is currently the redefined subroutine) into the packagetest namespace.
We also define the packagetest::callSampleFunction subroutine.
We call packagetest::callSampleFunction, which in turn
calls packagetest::sampleFunction, which is the redefined subroutine
from package2.pm
A guess regarding #1:
The output could come together if we executed the script normally but kept the interpreter with its global state alive, then recompile and re-execute sample.pl. In this case, package1.pm and package2.pm would not be re-executed because they are already loaded. The use package1 would then import the current package1::sampleFunction which already is the redefined version.
To test this hypothesis, restart your IDE and execute the script two times in debug mode. It should then output
from package1.pm
from package2.pm
during the first run, and
from package2.pm
from package2.pm
for all subsequent runs.
The real problem is
that you are redefining subroutines (don't do this),
that you are using the same package name package1 in different files (don't do this),
that you are using a filename package2.pm that does not correspond to the package name package1 inside (don't do this either), and
that you have a number of other style issues, including:
lowercase package names are reserved for “pragmatic modules”
don't export by default, instead use #EXPORT_OK and let the user of your module explicitly request some symbol
require "filename" is usually not something you want to do.
You did not use strict; use warnings
You are using warnings in production, but not in development? Why? :(
You are using the -w switch which is a bit oudated.

How to specify in the script to only use a specific version of perl?

I have a perl script written for version 5.6.1 and which has dependencies on Oracle packages, DBI packages and more stuff. Everything is correctly installed and works.
Recently perl version 5.8.4 got installed and because those dependencies are not correctly set so the script fails.
'perl' command points to /program/perl_v5.8.4/bin/perl -- latest version
So, when I have to run my perl script I have to manually specify in command prompt
/program/perl_v5.6.1/bin/perl scriptName.pl
I tried adding following lines in script:
/program/perl_v5.6.1/bin/perl
use v5.6.1;
But, this means script has to take Perl version > 5.6.1
I found couple of related question which suggested:
To export path. But, I want the script for all the users without have to export path
Specify version greater than. But, I want to use just one specific version for this script.
use/require commands. Same issue as above.
My question: How to specify in the script to only use a specific version of perl?
The problem is that the interpreter specified in the shebang line (#!/some/path/to/perl) is not used if perl script is called like this:
perl some_script.pl
... as the 'default' (to simplify) perl is chosen then. One should use the raw power of shebang instead, by executing the file itself:
./some_script.pl
Of course, that means this file should be made executable (with chmod a+x, for example).
I have in my code:
our $LEVEL = '5.10.1';
our $BRACKETLEVEL = sprintf "%d.%03d%03d", split/\./, $LEVEL;
if ($] != $currentperl::BRACKETLEVEL)
{
die sprintf "Must use perl %s, this is %vd!\n", $LEVEL, $^V;
}
These are actually two different modules, but that's the basic idea. I simply "use correctlevel" at the top of my script instead of use 5.10.1; and I get this die if a developer tries using the wrong level of perl for that product. It does not, however, do anything else that use 5.10.1; would do (enable strict, enable features like say, switch, etc.).

%ENV doesn't work and I cannot use shared library

I cannot use %ENV var on my Perl script to use Oracle libs.
BEGIN {
$ORACLE_HOME = "/usr/lib/oracle/10.2.0.3/client64";
$LD_LIBRARY_PATH = "$ORACLE_HOME/lib";
$ORACLE_SID="prod";
$ENV{ORACLE_SID}=$ORACLE_SID;
$ENV{ORACLE_HOME}= $ORACLE_HOME;
$ENV{LD_LIBRARY_PATH}= $LD_LIBRARY_PATH;
};
If I print $ENV{'ORACLE_HOME'} and $ENV{'LD_LIBRARY_PATH'} all seems ok but, when I run my script I have the error:
install_driver(Oracle) failed: Can't load '/usr/local/lib64/perl5/auto/DBD/Oracle/Oracle.so' for module DBD::Oracle: libclntsh.so.10.1: cannot open shared object file: No such file or directory at /usr/lib64/perl5/DynaLoader.pm line 200.
at (eval 3) line 3
Compilation failed in require at (eval 3) line 3.
Perhaps a required shared library or dll isn't installed where expected
at persistence.perl line 22
Searching on web I saw that the correct way to set env vars on Perl is to use %ENV hash.
Exporting ORACLE_HOME and LD_LIBRARY_PATH through unix shell (export LD_LIBRARY_PATH=...) it works correctly. Any advice?
The LD_LIBRARY_PATH environment variable has to be set before your program starts — before perl itself is loaded. Changing it in BEGIN{} will affect new programs that you start, but it won't affect the loading of shared libraries — in this case (although I've never used the DBD::Oracle) you're loading an Oracle .so into the already-running program, so it's “too late” to change the LD_LIBRARY_PATH. The dynamic linker /lib/ld.so (or so) is started before perl, so by the time your script is compiled and BEGIN{} runs, it's already set up.
You could try to re-exec your script as its own successor or something*, but a short shell script is almost certainly going to be the simplest solution:
#!/bin/sh
export LD_LIBRARY_PATH=/usr/lib/oracle/10.2.0.3/client64/lib
export ORACLE_SID=prod
exec /usr/local/bin/your-db-program "$#"
*- this would be kinda crazy, but TIMTOWTDI:
eval {
use DBD::Oracle foo bar baz; …
};
if ($# =~ /install_driver\(Oracle\) failed/) {
$ENV{LD_LIBRARY_PATH} .= ':/usr/lib/oracle/10.2.0.3/client64/lib';
$ENV{ORACLE_SID} = 'prod';
warn "Restarting with LD_LIBRARY_PATH reset:\n\n$#\n";
exec { $0 } $0 => #ARGV;
}
I wrote a few test scripts to verify that the environment is being set when you change %ENV:
use strict;
use warnings;
use feature qw(say);
BEGIN {
my $foo = "bar-bar";
$ENV{FOO} = "$foo";
}
system qq(/bin/echo printing out \$FOO);
This prints out:
printing out bar-bar
which is what I expected.
I then tried this:
use strict;
use warnings;
use feature qw(say);
BEGIN {
my $foo = "bar-bar";
$ENV{FOO} = "$foo";
}
system qq(./test.sh);
and created a test.sh program that looks like this:
#! /bin/sh
echo This is what I got: $FOO;
In this case, my Perl script is running test.sh which prints out the value of the $FOO environment variable that was set in my Perl script. Running test.pl I get:
This is what I got bar-bar
This shows that not only is Perl setting the environment variables, but that it is also exporting those variables, so called shell scripts have access to them.
You can try a similar technique to verify that both LD_LIBRARY_PATH and ORACLE_HOME are being set before they're used. I suspect you'll find that this is indeed happening, but that your program still isn't working when you set %ENV.
This points to one conclusion: Setting the environment for LD_LIBRARY_PATH and ORACLE_HOME might be occurring too late by the time your Perl script starts. I believe the operating system examines LD_LIBRARY_PATH before Perl starts. I found this doing a search on LD_LIBRARY_PATH:
LD_LIBRARY_PATH is an environment variable you set to give the run-time shared library loader (ld.so) an extra set of directories to look for when searching for shared libraries. Multiple directories can be listed, separated with a colon (:). This list is prepended to the existing list of compiled-in loader paths for a given executable, and any system default loader paths.
So, LD_LIBRARY_PATH is for the ld.so runtime shared library loader, If ld.so has already been loaded, changing LD_LIBRARY_PATH won't do anything.
I found a similar discussion on Perl Monks. I noticed someone found rerunning env seemed to work.
One solution is to modify /etc/ld.so.conf
On CentOS/RHEL 6.4, you could create etc/ld.so.conf.d/oracle with this:
/oracle/sw/product/11.2.0/dbhome_1/lib
Obviously, modify as suits your ORACLE_HOME.
Then run
ldconfig -v
You could put the export commands into the start up script for your unix shell which you should have permission to edit. That way, the environment variables will be set whenever you start a new shell and all scripts and programs that use Oracle will pick them up.
I just went through something similar. I had to make sure that the Oracle environment was setup before anything else called it. Make sure the BEGIN block is before any other "use" statements. In my case, something was being called in Apache's httpd.conf file, so I had to setup my environment there instead of in my package.