Why am I getting "Use of uninitialized value $num in subtraction"? - perl

I'm working on a Perl module using XS to bind to a C library. When I build it, I'm getting a warning message saying Use of uninitialized value $num in subtraction (-) at /usr/lib/perl5/vendor_perl/5.14.2/ExtUtils/ParseXS.pm line 1769, <GEN8> line 90.
This is triggered by the code generated by ExtUtils::Constant. Commenting out the INCLUDE: const-xs.inc line in Foo.xs removes the warning. But I don't know whether the bug is in ExtUtils::ParseXS or in ExtUtils::Constant.
I'm using ExtUtils::Constant 0.23, ExtUtils::ParseXS 3.15, Module::Build 0.38, and Perl 5.14.2.
I've managed to pare it down to a reasonably small test case that doesn't require any external C library, but it's still too large to post here. I've placed it in a GitHub repo. To reproduce the bug, clone the repo, type perl Build.PL and then ./Build. You should see:
$ perl Build.PL
Regenerating constants...
Created MYMETA.yml and MYMETA.json
Creating new 'Build' script for 'Foo' version '0.01'
$ ./Build
Building Foo
Use of uninitialized value $num in subtraction ...
gcc ... -o lib/Foo.o lib/Foo.c
ExtUtils::Mkbootstrap::Mkbootstrap('blib/arch/auto/Foo/Foo.bs')
gcc ... -o blib/arch/auto/Foo/Foo.so lib/Foo.o
You'll only see "Regenerating constants..." if you have ExtUtils::Constant installed. You shouldn't need it to reproduce the bug, because I've added the generated files to the repo.
Whatever the problem is, it doesn't seem to stop the code from working, because the included test does pass.
Update: I've reported this as RT#112776. The consensus seems to be it's a bug in ExtUtils::ParseXS, but the solution is not clear.

Related

Specify shebang on project using Perl Module::Build

I'm packaging clusterssh to openSUSE and need to change default shebang from #!/usr/bin/env perl to #!/usr/bin/perl. clusterssh uses Module::Build.
I'll probably use the patch (as Debian package do), but I wonder easy usage for fix_shebang_line(#files) in RPM packaging.
It's already called during the building process.
Basically, uninstalled scripts should use #!/usr/bin/perl or #!perl, and the installation process should rewrite that to the point to the perl used to run the installer. That way, a script installed by /usr/bin/perl will use /usr/bin/perl, and a script installed using /home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl will use /home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl.
(This applies to both the Module::Build installer and the ExtUtils::MakeMaker installer.)
Note that the documentation for fix_shebang_line says it doesn't touch a shebang line of #!/usr/bin/env perl (because it's not recognized as invocation of perl), so simply patching the scripts to use #!/usr/bin/perl instead of #!/usr/bin/env perl does the trick.
With that change, the install-ready staging directory (blib) produced by ./Build will contain the edited files.
$ perl -e'CORE::say $^X'
/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
$ for fn in ccon crsh csftp cssh ctel; do printf '%-6s ' "$fn:"; head -n 1 "bin_PL/$fn"; done
ccon: #!/usr/bin/perl
crsh: #!/usr/bin/perl
csftp: #!/usr/bin/perl
cssh: #!/usr/bin/perl
ctel: #!/usr/bin/perl
$ perl Build.PL
Could not get valid metadata. Error is: ERROR: Missing required field 'dist_abstract' for metafile
Could not create MYMETA files
Creating new 'Build' script for 'App-ClusterSSH' version 'v4.13.203'
$ ./Build
Building App-ClusterSSH
Using perl binary: /home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
Using perl version v5.26.2
Generating: /home/ikegami/tmp/clusterssh/bin_PL/cssh
Generating: /home/ikegami/tmp/clusterssh/bin_PL/csftp
Generating: /home/ikegami/tmp/clusterssh/bin_PL/ccon
Generating: /home/ikegami/tmp/clusterssh/bin_PL/crsh
Generating: /home/ikegami/tmp/clusterssh/bin_PL/ctel
Generating: /home/ikegami/tmp/clusterssh/bin_PL/clusterssh_bash_completion.dist
$ for fn in ccon crsh csftp cssh ctel; do printf '%-6s ' "$fn:"; head -n 1 "blib/script/$fn"; done
ccon: #!/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
crsh: #!/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
csftp: #!/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
cssh: #!/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
ctel: #!/home/ikegami/usr/perlbrew/perls/5.26.2t/bin/perl
I didn't bother running ./Build install, whose main task is to copy the files from the staging directory into their final locations. Besides, that part will need to be replaced by your package manager anyway (assuming you're simply placing the contents of the blib directory into your package).
If you somehow need to do it yourself, you could use the following:
find bin -type f \
-exec perl -i -pe'
s/^#!\S*perl\S*/#!$^X/ if $. == 1;
close ARGV if eof;
' {} +
Notes:
Use the perl you wish the scripts to use.
GNU tools assumed; adjust as necessary.
close ARGV if eof; resets the line number ($.) for each file.
eof is different than eof(), and only the former will work here.
The line breaks are optional and may be removed.

Error when executing 'perl Makefile.PL': Can't find net-snmp-config

I am trying to install NetSNMP-OID (version 5.0404) but when I run 'perl Makefile.PL' (in the NetSNMP-OID directory) I get the message:
You need to install net-snmp first (I can't find net-snmp-config) at Makefile.PL line 91.
After adding some checkpoints to the Makefile.PL using 'print' statements, I discovered that it was not executing the following lines (83 to 85):
if (lc($opts->{'insource'}) eq "true") {
$Params{'LIBS'} = "-L../../snmplib/.libs -L../../snmplib/ " . $Params{'LIBS'};
$Params{'CCFLAGS'} = "-I../../include " . $Params{'CCFLAGS'};
which is likely why it crashed at 90/91?:
if ($Params{'LIBS'} eq "" || $Params{'CCFLAGS'} eq "") {
die "You need to install net-snmp first (I can't find net-snmp-config)";
}
I have net-snmp installed (correctly, I think), so I'm wondering if maybe I have it in the wrong directory? It is currently in home/ (~, i.e. ~/net-snmp-5.7.3/).
If someone with experience in Perl could explain what the first segment of code (lines 83 to 85) is doing, that would be beneficial as well!
OS: Raspbian
EDIT:
Notes:
net-snmp-config is a shell (.sh) script found directly inside the net-snmp directory (~/net-snmp-5.7.3/net-snmp-config).
Makefile.PL can be found here: NetSNMP-OID-5.0404/Makefile.PL
I haven't used the module myself, but from a quick look at the source, try
perl Makefile.PL --NET-SNMP-CONFIG ~/net-snmp-5.7.3/net-snmp-config
%opts come from GetOptions, which parses command-line arguments (source).
%Params are produced based on the %opts, the machine type, and other factors. For example, $Params{LIBS} is assigned here by running net-snmp-config.
The NET-SNMP-CONFIG command-line option is stored into $opts{nsconfig}, which is then run to get the CFLAGS and LIBS necessary.
Edit Lines 83–85 are only one of the ways CFLAGS and LIBS can be set. In your case, it appears that lines 77 and 79 are not able to do so, probably because they can't find net-snmp-config.

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.

Installing ancient perls with Perlbrew

I'm trying to use perlbrew to install some older Perls. I believe it's failing due to the old-style version numbers. According to perlbrew available:
perl-5.14.3-RC1
perl-5.16.1
perl-5.14.2
perl-5.12.4
perl-5.10.1
perl-5.8.9
perl-5.6.2
perl5.005_04
perl5.004_05
perl5.003_07
However, when I run perlbrew install perl5.003_07, I get:
Unknown installation target "perl5.003_07", abort. Please see
`perlbrew help` for the instruction on using the install command.
If I try it by giving a direct link to the tarball, e.g. perlbrew install http://www.cpan.org/src/5.0/perl5.005_03.tar.gz, it downloads the tarball but the regex for parsing the version number fails:
Use of uninitialized value $dist_version in concatenation (.) or string at /usr/local/share/perl5/App/perlbrew.pm line 686.
Fetching perl- as /home/cpanci/perl5/perlbrew/dists/perl5.005_03.tar.gz
Use of uninitialized value $dist_version in pattern match (m//) at /usr/local/share/perl5/App/perlbrew.pm line 925.
Installing /home/cpanci/perl5/perlbrew/build/perl5.005_03 into ~/perl5/perlbrew/perls/perl-
This could take a while. You can run the following command on another shell to track the status:
tail -f ~/perl5/perlbrew/build.perl-.log
Use of uninitialized value $dist_version in pattern match (m//) at /usr/local/share/perl5/App/perlbrew.pm line 952.
Use of uninitialized value $dist_version in pattern match (m//) at /usr/local/share/perl5/App/perlbrew.pm line 969.
Installing /home/cpanci/perl5/perlbrew/build/perl5.005_03 failed. Read /home/cpanci/perl5/perlbrew/build.perl-.log to spot any
issues.
Any ideas? It's working fine for newer perls.
This is with App::perlbrew version 0.52.
I think you're stuck having to hack on perlbrew. I can get some ways by renaming the tarball perl-5.5.3.tar.gz and making a symlink in perl5/perlbrew/build like so:
lrwxrwxrwx 1 darch users 12 Oct 8 14:16 perl-5.5.3 -> perl5.005_03
, but at that point it tries to run 5.5.3's Configure with options it doesn't understand. It doesn't look to me like trying to build such old Perls with perlbrew is actually supported, for all that it does cheerfully list them.

Archive::Extract Module gives warnings in perl v5.12.3 on mac os x lion

I am using the default perl distribution(version 5.12.3) that came bundled with os x lion. When I use the module Archive::Extract in scripts with the -W module it gives the following warnings:
Subroutine version::declare redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 139.
Subroutine version::qv redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 140.
Subroutine version::vcmp redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 142.
Subroutine version::stringify redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 145.
Subroutine version::("" redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 146.
Subroutine version::new redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 147.
Subroutine version::parse redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 148.
Subroutine UNIVERSAL::VERSION redefined at /Library/Perl/5.12/darwin-thread-multi-2level/version.pm line 194.
Name "IPC::Cmd::I18N::en::Lexicon" used only once: possible typo at /Library/Perl/Updates/5.12.3/Locale/Maketext.p\
m line 444.
Name "Archive::Extract::I18N::en::Lexicon" used only once: possible typo at /Library/Perl/Updates/5.12.3/Locale/Ma\
ketext.pm line 444.
Name "Win32::Locale::Lexicon" used only once: possible typo at /System/Library/Perl/5.12/I18N/LangTags/Detect.pm l\
ine 140.
Name "Params::Check::I18N::en::Lexicon" used only once: possible typo at /Library/Perl/Updates/5.12.3/Locale/Maket\
ext.pm line 444.
Name "Module::Load::Conditional::I18N::en::Lexicon" used only once: possible typo at /Library/Perl/Updates/5.12.3/\
Locale/Maketext.pm line 444.
I tried running upgrade in CPAN. I even deleted my .cpan directory and reconfigured my CPAN. I tested the module and it seems to work fine. But somehow I am uncomfortable with the warnings.
An other problem which made me post this question is some of my other modules are not being upgraded (via CPAN of course). This never happened to me when I was running linux.
Failed during this command:
PMQS/BerkeleyDB-0.49.tar.gz : make NO
MIKER/NetAddr-IP-4.058.tar.gz : make_test NO
DMR/DProf-19970614.tar.gz : make NO
NWCLARK/perl-5.8.6.tar.gz : make NO isa perl
JESSE/perl-5.13.9.tar.gz : make NO isa perl
SHERZODR/Class-PObject-2.17.tar.gz : make_test NO
GBARR/IO-Tty-0.04.tar.gz : writemakefile NO '/usr/bin/perl Makefile.PL' returned status 65280
DOY/Moose-2.0004.tar.gz : make_test NO
RJBS/perl-5.15.2.tar.bz2 : make NO isa perl
FLORA/perl-5.15.4.tar.gz : make NO isa perl
Should I try to clean up the default perl installation and use homebrew or macports to do a clean install? Is there something obvious I am missing which is causing all the problems?
Thanks for your help.
From perlrun...
-W Enables all warnings regardless of "no warnings" or $^W.
You got what you asked for.
You shouldn't be using -W in normal operation. Sometimes modules deliberately turn off warnings for certain things, because they know what they're doing will trigger them.
Try -w instead.
As for your other question... sometimes CPAN modules fail to install. A change of Perl version or operating system can do this. You'll have to look at the verbose output of the install attempts. Or you can look Module::Name to be dropped into a shell in the source tarball of Module::Name where you can test and debug manually.
That said, it's recommended to leave the operating system supplied Perl alone and install a fresh one for development. This allows you to make whatever changes you want, install whatever modules you want and whatever version of Perl you want without risking messing up your operating system or having your upgrades blown over in the next OS upgrade.
perlbrew is one of the best ways to handle that.