Eclipse: Perl EPIC debug & release mode execution mismatch - eclipse

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.

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 to test missing optional perl modules via the command line perl call

I have a test suite with hundreds of tests for a perl module I've yet to release which is a command line interface. Since it is a command line interface, the tests (until possibly now) are all written to drop code into a template and then call the template script using a system call.
I recently added an optional dependency on a 3rd party module that's not a part of core perl. I know my module works whether that module is installed or not because I have a computer with it installed and one without and the module works without error in each case. However, I'd like to be able to write a test to confirm that my module will work when the 3rd party module is absent - and I'd like that test to work even if the 3rd party module is installed, but behave as if it wasn't.
Ideally, I could use the structure I've put in place for testing which makes a system call to a template script. I know I could write a separate test script that manipulates #INC in the BEGIN block, imports the particular methods that use the module, and call them like a unit test. But I would like to know if there's a way I can use the test structure I've already got all my other tests using, which is to make a system call.
So is there a way to exclude a module from being imported via a perl command line option? I've tried -M-Module, but the code use Module still imports the module.
Incidentally, my module uses the 3rd party module inside an eval, which is how I made it optional.
I wrote Test::Without::Module for this exact case. It works by modifying #INC to prevent loading of modules that you name. For testing, you could either run the test from the command line:
perl -MTest::Without::Module=Some::Module -w -Iblib/lib t/SomeModule.t
Or allow/disallow loading the module from within your test suite:
use Test::Without::Module qw( My::Module );
# Now, loading of My::Module fails :
eval { require My::Module; };
warn $# if $#;
# Now it works again
eval q{ no Test::Without::Module qw( My::Module ) };
eval { require My::Module; };
print "Found My::Module" unless $#;

failed to install my own perl module(pm file)

I have made my own perl modules(pm files),named test.pm
package test;
use Exporter;
use strict;
use File::Basename qw(basename dirname);
use Cwd qw(abs_path);
use File::Path qw(make_path);
use FindBin qw($Bin $Script);
BEGIN {
our #ISA = qw(Exporter);
our #EXPORT = qw(mkdirOrDie);
our $VERSION = 1.0;
}
sub mkdirOrDie
{
my ($dir) = #_ ;
if(!-d $dir){
make_path($dir);
$dir=abs_path($dir);
# timeLog("Directory Created: $dir");
}
}
and I tried to install this module as follows,
h2xs -AX -n test
perl Makefile.PL
make
make install
there is no error,and I copy the test.pm to /usr/lib64/perl5/5.10.0/,but when i call sub function using test, an error has occurred,
Undefined subroutine &main::mkdirOrDie called at /to/my/path/main.pl line 92
is there something i ignored?
It's unclear at which point things started to go wrong for you.
Firstly, test.pm is a bad name for a Perl module. Perl modules should have names that begin with upper case letters (and Test.pm is already taken).
You should run h2xs before writing your code - as it generates a module skeleton for you fill in. I hope it hasn't overwritten your code with an almost empty file! It's also worth noting that most people stopped using h2xs many years ago. These days we have tools like Module::Starter.
Then, running, make install (which you need to do with root permissions - so usually with sudo) is what installs your module into the system libraries. There should be no need to run that cp command afterwards.
As for why your code doesn't find the module, there are many possible reasons. Are you using Perl 5.10 or do you have other Perl versions installed? What does the code look like that you are trying to use? Does test.pm still include the code you think it does?
Need more information to be much help here.

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.

Perl 5: namespace issues when `use`ing SWIG-generated module in declared package

I'm having namespace issues with a Perl module. When I use it in a regular script file, all public symbols are imported into the (implicit) main:: package as expected. But when I try to use it in a source file with a package declaration of its own (i.e. typically another module), weird stuff starts to happen.
The module in question can be found on CPAN as Ufal::MorphoDiTa. It is a set of bindings to a C++ library, auto-generated using SWIG. No need to have the lib itself installed to reproduce the test cases below.
First then, a regular script file with no package declaration:
# script.pl
use Ufal::MorphoDiTa qw(:all);
use Data::Dumper;
# a closer look at symbols inside the main:: package
my %morph_in_main = %main::{ grep { /morph/i } keys %main:: };
print "main:: namespace:\n", Dumper \%morph_in_main;
# Morpho:: is exported by Ufal::MorphoDiTa
Morpho::load('foo');
As expected, the symbols from Ufal::MorphoDiTa are imported into main:: and the Morpho::load subroutine gets called (with no visible output, but that's fine):
$ perl script.pl
main:: namespace:
$VAR1 = {
'Morpho::' => *{'Ufal::MorphoDiTa::Morpho::'},
'_<morphodita/morphodita_perl.cpp' => *{'::_<morphodita/morphodita_perl.cpp'},
'_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle' => *{'::_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle'}
};
Let's add a package declaration now:
# Qux.pm
package Qux;
use Ufal::MorphoDiTa qw(:all);
use Data::Dumper;
# a closer look at symbols inside the main:: package
my %morph_in_main = %main::{ grep { /morph/i } keys %main:: };
print "main:: namespace:\n", Dumper \%morph_in_main;
# a closer look at symbols inside the Qux:: package
my %morph_in_qux = %Qux::{ grep { /morph/i } keys %Qux:: };
print "Qux:: namespace:\n", Dumper \%morph_in_qux;
# Morpho:: is exported by Ufal::MorphoDiTa
Morpho::load('foo');
As you can see below, in this case, some of the imported symbols wind up in the main:: package and some in the declared Qux:: package (perhaps this is intended behavior?):
$ perl Qux.pm
main:: namespace:
$VAR1 = {
'_<morphodita/morphodita_perl.cpp' => *{'::_<morphodita/morphodita_perl.cpp'},
'Morpho::' => *{'::Morpho::'},
'_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle' => *{'::_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle'}
};
Qux:: namespace:
$VAR1 = {
'Morpho::' => *{'Ufal::MorphoDiTa::Morpho::'}
};
Undefined subroutine &Morpho::load called at Qux.pm line 11.
At any rate, as indicated by the last line of the output, Perl suddenly can't find the subroutine anymore. Notice all we really did was add the package declaration before all use statements.
Now the cherry on top -- if we use Ufal::MorphoDiTa before declaring package Qux, everything starts working again:
# Qux.pm
use Ufal::MorphoDiTa qw(:all);
package Qux;
use Data::Dumper;
# etc.
The output of running the module with perl Qux.pm is the same as in the first case, i.e. the sub Morpho::load is found, in spite of not being prefixed with the main:: namespace into which it was loaded. Contrast this with the behavior of a standard module like Data::Dumper -- when that is loaded before the package declaration, the sub Dumper has to be referred to as main::Dumper when in package Qux::.
I'd appreciate any pointers as to what's happening here... It's not that I can't work around it, but the issue is bugging me -- I'm not sure whether it's a quirk of Perl, a bug on the SWIG side of things (I don't have enough Perl-Fu to make sense of the auto-generated bindings module, which has package declarations all over the place), or whether (another plausible alternative) my own ignorance is at fault here. Thanks for any input! :)
So, it turns out that this is expected behavior, as explained pretty succinctly in perldoc perlmod (emphasis mine):
Packages may themselves contain package separators, as in $OUTER::INNER::var . This implies nothing about the order of name lookups, however. There are no relative packages: all symbols are either local to the current package, or must be fully qualified from the outer package name down. For instance, there is nowhere within package OUTER that $INNER::var refers to $OUTER::INNER::var. INNER refers to a totally separate global package.
Now, the symbols exported by the Ufal::MorphoDiTa module all live inside various subnamespaces (e.g. the load subroutine in the Morpho:: namespace), so they must always be fully qualified (they are not local to the package into which they're imported). What is potentially confusing is that a main:: prefix is implicit for symbol lookup, so without a package declaration (i.e. in a regular script), everything works fine, because calling Morpho::load is a shortcut for main::Morpho::load (and the module was indeed loaded inside main::).
But when the module is imported into package Qux::, Morpho::load must be referred to as Qux::Morpho::load because, to paraphrase the perldoc, "there is nowhere within package Qux that Morpho::load refers to Qux::Morpho::load. Morpho refers to a totally separate global package [main::Morpho]" -- which does not exist, because Morpho::load was loaded inside Qux::, not inside main::.
This explains all of the apparent quirks cited above. A tip of the hat to the good folks over at the MorphoDiTa project for their help and responsiveness in solving this conundrum!