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

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

Related

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

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.

How do I import a perl module outside of #INC that does not end in .pm?

Background
I am attempting to import a perl module that does not end in .pm with a method similar to this answer:
use lib "/hard/coded/directory"; use scripting;
However, when I attempt to import a module in this way, I get the following error when running perl -c:
Can't locate scripting.pm in #INC (#INC contains: ... ... ... /hard/coded/directory) at name of script line 47.
BEGIN failed--compilation aborted at name of script line 47.
Question
How do I import a perl module outside of #INC that does not have .pm at the end of the file?
If the file has a package directive, the file name and the package directive need to match, so simply fix the file name.
If the file doesn't have a package directive, you don't have a module, and you shouldn't use use or require. This can cause problems.
What you have is sometimes called a library, and you should use do.
do('/hard/coded/directory/scripting')
or die $# || $!;
(For proper error checking, the file needs to result in a true value.)
That said, you are probably trying to do something really awful. I'm guessing you're either have a configuration file written in Perl or a poorly written module. Perl is not a suitable choice of language for a configuration file, and avoiding namespaces is just bad programming with no benefit.
If the source file does not define new namespaces or classes and you just want to read the function definitions or data from a file, Perl provides the do and require functions.
do "scripting";
require "scripting";
The difference between them is that require will look for the file to evaluate to a true value (it expects the last statement in the file to resolve to a non-zero, non-empty value), and will emit a fatal error if this does not happen. (You will often see naked 1; statements at the end of modules to satisfy this requirement).
If scripting really contains class code and you do need all the functionality that the use function provides, remember that
use Foo::Bar qw(stuff);
is just syntactic sugar for
BEGIN {
$file = <find Foo/Bar.pm on #INC>;
require "$file";
Foo::Bar->import( qw(stuff) )
}
and suggests how you can workaround your inability to use use:
BEGIN {
require "scripting";
scripting->import()
}
In theory, the file scripting might define some other package and begin with a line like package Something::Else;. Then you would load the package in this module with
BEGIN {
require "scripting";
Something::Else->import();
}

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.

Library issue while using perl2exe

I am trying to convert my Perl script to standalone exe.
I assume perl2exe is a tool that serves this purpose. More or less.
When I am trying to generate the exe file, I am getting library issues.
One of the library issues is:
Warning: Can't locate VMS/Stdio.pm
at C:\Perl\lib\File\Temp.pm line 19
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
When I went to line 19 of Temp.pm, the line is written as follows:
require VMS::Stdio if $^O eq 'VMS';
But,my OS is MSWin32.
I am coming to a conclusion that, perl2exe is not compiling the script properly. Its reading my OS wrong.
Sample script is as follows:
my_libraries.pl
use Tk;
use lib 'C:\Perl\lib\Digest';
use strict;
use strict;
use warnings;
use strict;
use warnings;
use LWP::Simple qw(getstore);
use LWP::UserAgent;
use Digest::MD5 qw( md5_hex );
use Digest::MD5::File qw( file_md5_hex );
use File::Fetch;
use WWW::Mechanize ;
use Tk::ErrorDialog;
c:\perl2exe\perl2exe-16.00-Win> perl2exe my_libraries.pl my_libraries.exe
Warning: Can't locate File/BSDGlob.pm
at C:\Perl\lib\File\GlobMapper.pm line 13
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Warning: Can't locate Digest/Perl/MD5.pm
at C:\Perl\lib\Digest\MD5.pm line 30
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Warning: Can't locate VMS/Stdio.pm
at C:\Perl\lib\File\Temp.pm line 19
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Warning: Can't locate VMS/DCLsym.pm
at C:\Perl\lib\IPC\Cmd.pm line 227
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Warning: Can't locate VMS/Filespec.pm
at C:\Perl\lib\ExtUtils\Manifest.pm line 31
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Warning: Can't locate HTML/FormatText.pm
at C:\Perl\lib\HTML\Element.pm line 1297
#INC = C:\Perl\site\lib, C:\Perl\lib, ., C:\Perl\lib\Digest, must be directory, not file)
Sorry. let me put my obvious question here:
Why is perl2exe giving library issues which are not intended to come? Is it a bug in perl2exe or am I doing something wrong?
I mean, you can see in line 19 that if the OS is 'VMS', then stdio.pm is required. My os is 'MSWin32'.
I tried a to z possible remediation to make the perl2exe work. I removed the sections that was producing warnings (Hacked the modules). Studied and tried various flags. I have to say it is not at all feasible to convert Perl programs using diverse modules to exe files using perl2exe.
I found a software that exactly did what I wanted- Cava Packager.
It took sometime to find the below page-
How can I package my Perl script to run on a machine without Perl?
It converted my Perl program to Exe and also generated an installation file. Awesome.
Thanks,
Anoop.
The problem is
C:\Perl\lib\File\Temp.pm line 19
Open the file you will see this
require VMS::Stdio if $^O eq 'VMS';
Change the file not read-only, then place # for this line, go back to perl2exe the file again, then it should be gone.
It may be of interest to readers of this issue that in addition to the VMS/Stdio.pm error, I also received "Can't locate the.pm". The line in my perl code that it pointed to was the text "Use the 't' command..." that was inside a double-quoted print statement. Apparently perl2exe looked for the 'use' statement regardless of where in my code it appeared. The fix was to either re-word the text to remove the word 'use' or put the text in single quotes.

Run time error in perl

I have made a standalone exe file by packaging a perl file using PAR::Packager.
The file works perfectly in my system, but it throws an error. if i used it in another system which have windows xp as OS.
The locale codeset (cp936) isn't one that perl can decode,Stopped at Encode/Locale.pm line 94
Compliation failed in require at LWP/UserAgent.pm line 1000
Please give some suggestion.Thanks
Update :
the files i have included in the script is
use Encode::Byte;
use strict;
use warnings;
use WWW::Mechanize;
use utf8;
You are missing the following line in your code:
use Encode::CN;
As you can see in Encode::Byte documentation, cp936 it is not included. But it is listed in Encode::CN docs.
Adding both packages (Encode::Byte and Encode::CN should solve your problem).