How to have conditional libraries with Perl? [duplicate] - perl

This question already has answers here:
Perl - Include package from relative path, which includes another package from a relative path?
(4 answers)
Closed 8 years ago.
I have homemade #Perl libraries that I'm moving from machine to machine, and the paths are not the same from place to place. Specifically, on the old machines, they existed in /home/foo/lib/, while they're moving to /group/bar/apps/lib on the new machines, and I now want to have them in something like /home/me/dev/Tools/lib.
What we did was have multiple use lib lines. /home/foo isn't available on the new machine, and /group/bar isn't a directory on the old machine, so when it sees this --
use lib '/home/foo/lib/' ;
use lib '/group/bar/apps/lib' ;
use Tools::Foo ;
-- everything is fine.
The problem is, they link to each other, and I'd rather not have something in /home/me/dev/Tools/lib load a program from /group/bar/apps/lib, and when I move this stuff to production, I don't want to have anything pointing back to ~/me/dev. Preferrably, I would want to not have to modify the code when I move it into production, so that, when everything is deployed, diff /group/bar/apps/lib/Tools/Foo.pm /home/me/dev/Tools/lib/Tools/Foo.pm would be empty.
So, how do I set things for multiple conditional library locations?

Options:
Properly install the modules.
Place your modules relative to the script
use FindBin qw( $RealBin );
use lib "$RealBin/../lib"; # Or whatever.
Use environment variable PERL5LIB rather than use lib.
The statements could be placed in sitecustomize.pl (if support for sitecustomize.pl was enabled when perl was built).

Using the following pragma
package lib_first_of;
use lib ();
use strict;
use warnings;
use Carp;
sub import {
foreach my $path (#_) {
if (-d $path) {
lib->import($path);
return 1;
}
}
croak "$0: no suitable library path found";
}
1;
lets your main program have the form
#! /usr/bin/env perl
use strict;
use warnings;
use lib_first_of (
"/home/foo/lib",
"/group/bar/apps/lib",
);
use MyModule;
print "done.\n";
If neither path is present, the program fails with errors resembling
my-program: no suitable library path found at my-program line 7
BEGIN failed--compilation aborted at my-program line 9.

Related

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.

I want to create a word count module and I want to reuse it further

I want to create a module in Perl. The below code is not working properly. I want to create a word count module and I want to reuse it further. Can anyone help me out to create this module? This is my first attempt to create a module so kindly help me out.
package My::count
use Exporter qw(import);
our #Export_ok = qw(line_count);
sub line_count {
my $line = #_;
return $line;
}
I saved the above code in count.pm
use My::count qw(line_count);
open INPUT, "<filename.txt";
$line++;
print line count is $line \n";
I saved the above script in .pi extension.
This code is showing error when I run it on an Ubuntu platform. Kindly help me to fix this errors.
Perl scripts are stored with .pl extension. As you say use My::count qw(line_count); Perl tries to search the modules from the directories stored in #INC variable. You can run it with the -I flag to specify the directory to search the custom packages. Refer to this question for more info.
By convention Perl packages usually have a capitalized first letter, so My::count is more in keeping with convention if you call it package My::Count;. Typically lower-cased module names are reserved for pragmas such as 'strict' and 'warnings'. So go ahead and change the name to My::Count.
Next, save the module in a path such as lib/My/Count.pm. lib is by convention as well.
Then you have to tell your script where to find package My::Count.
Let's assume you're storing your module and your executable like this:
~/project/lib/My/Count.pm
~/project/bin/count.pl
Notice I also used a .pl extension for the executable. This is another convention. Often on Unix-like systems people omit the .pl extension altogether.
Finally, in your count.pl file you need to tell perl where to find the library. Often that is done like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use My::Count 'line_count';
# The rest goes here...
As you can see, we're using FindBin to locate where the executable is stored, and then telling perl that it should look (among other places) in the lib folder stored in a relative location to the executable.
Naturally, as this is Perl, this is not the only way to do it. But it's one common idiom.
You need to move your count.pm file into a directory called My. So you have the following.
./count.pl
./My/count.pm

A proper way of using Perl custom modules inside of other Perl modules

I'm using custom modules in my scripts and have to store them outside of Perl lib directory. So in Perl scripts (*.pl) I use the following block to include them in #INC:
BEGIN {
use FindBin qw($Bin);
push #INC, "$Bin/../ModulesFolder1";
push #INC, "$Bin/../ModulesFolder2";
}
But I also have to use modules inside of my other Perl modules (*.pm), and as I understand FindBin works for scripts only. So I change that block to:
BEGIN {
push #INC, catdir( dirname( $INC{'ThisModule.pm'} ), qw( .. ModulesFolder1 ) );
push #INC, catdir( dirname( $INC{'ThisModule.pm'} ), qw( .. ModulesFolder2 ) );
}
It works but with a little problem. I code in Eclipse with EPIC plugin, and "if you have something in a BEGIN block that causes the compiler to abort prematurely, it won't report syntax errors to EPIC", so that way I loose Perl syntax check in modules.
So, with FindBin (in scripts) I don't have to use any functions (like catdir) in BEGIN{} block, and the syntax check of the following code goes on correctly. Besides, I'd like not to change any environment variables (like PERL5LIB), so that I could use the scripts on my colleagues' machines without any additional preparations.
What's the proper way of using custom Perl modules inside of other modules, and not interfering with EPIC syntax check at the same time? Or maybe I even should include modules in completely other way?
I strongly disagree with modifying #INC in modules. It causes all kinds of headaches. Let the script (or even the calling process via the PERL5LIB environment variable) setup #INC correctly.
script.pl:
use FindBin qw( $RealBin );
use lib
"$RealBin/../ModulesFolder1",
"$RealBin/../ModulesFolder2";
use ModuleInFolder1;
ModuleInFolder1.pm:
use ModuleInFolder2; # Works fine.
As for EPIC, do the following:
Right-click on the project.
Properties
Perl Include Path
${project_loc}/ModulesFolder1, Add to list
${project_loc}/ModulesFolder2, Add to list
(I literally mean the 14 chars ${project_loc}. That means something to EPIC. It will continue to work even if you move the project.)
PS — $RealBin is better than $Bin because it allows you to use a symlink to your script.
PS — __FILE__ is more appropriate than $INC{'ThisModule.pm'}.
Not sure about Eclipse, but you can use use lib (will probably not work, it changes #INC at compile time) or set the environment variable PERL5LIB to point to your library folder(s).
Set up PERL5LIB environment variable. Every time you use or require, Perl will check all directories listed in it.
Alternatively, place all necessary custom modules under script's directory, so you can use relative paths in use lib. It will also allow you to quickly make a bundle to transfer everything to another PC by just packing recursively from top-level directory.
Another solution (from my colleague) - a change to be made in the module:
sub path_to_current_module() {
my $package_name = __PACKAGE__ .'.pm';
$package_name =~ s#::#/#g;
for my $path ( #INC ) {
# print "\$path == '$path'\n";
if ( -e catfile( $path, $package_name ) ) {
return $path;
}
}
confess;
}
BEGIN {
my $path_to_current_module = path_to_current_module();
push #INC, catdir( $path_to_current_module, qw( .. ModulesFolder1 ) );
push #INC, catdir( $path_to_current_module, qw( .. ModulesFolder2 ) );
}
It seems that the old way (described in the question) Perl couldn't locate current module name in #INC - that's why perl -c was interrupted by error inside of the BEGIN block. And the described sub helps it to determine the real path to the current module. Besides, it doesn't depend on the current file name and can be copied to another module.

Using custom modules in PERL

I made myself a custom PERL module and it works when called by a script in the same directory, but not from outside the directory for somewhat obvious reasons. How do I use the module without installing it? eg:
use 5.012;
use warnings;
use Y:/my/dir/to/module.pm;
use lib 'Y:/my/dir/to';
use module; # BAD name for module, lowercase is reserved for pragmas...
If you only have one module, instead of using lib, you can do this :
BEGIN {
unshift #INC,"dir";
##INC is the directory list, where perl searches for .pm files
}
use Foo::Bar; #dir/Foo/Bar.pm
#or
do "dir/Foo/Bar.pm"; #perldoc -f do

How can I split my Perl code across multiple files?

My scripts are getting too long. How do I split my code (procedural subs) into multiple Perl files and tell the interpreter to make sense of them?
Kind of like:
# -> main.pl
#include "foo.pl"
say_hello();
and:
# -> foo.pl
sub say_hello {print "hello!"}
What you want to do is create one or more modules. Start by looking over perlmod, especially the Perl Modules section.
Since you say you're writing procedural code, you'll want to export functions from your modules. The traditional way to do that is to use Exporter (which comes with Perl), although Sub::Exporter is a newer CPAN module that allows for some nice things. (See also its Sub::Exporter::Tutorial for an introduction to exporting functions.)
Modules can be placed in any of the directories listed in the #INC variable. Try perl -V to get a list. You can also use lib to add directories at runtime. One trick is to use the FindBin module to find the location of your script, and then add a directory relative to that:
use FindBin; # Suppose my script is /home/foo/bin/main.pl
use lib "$FindBin::Bin/lib"; # Add /home/foo/bin/lib to search path
Your sample code, converted to a module:
In main.pl:
#! /usr/bin/perl
use strict;
use warnings;
use Foo;
say_hello();
In Foo.pm:
package Foo;
use strict;
use warnings;
use Exporter 'import';
our $VERSION = '1.00';
our #EXPORT = qw(say_hello);
sub say_hello {print "hello!"}
1; # A module must end with a true value or "use" will report an error
I think you may be looking for do? http://perldoc.perl.org/functions/do.html
put it in the same folder as your class and add use ClassName to the top of the calling file.
Also check the Perl OOP tutorial.