How do I use a Perl module from a relative location? - perl

I have a dir called foo, and in that I have lib and bin. The scripts in bin need stuff in lib. I do something like this:
#!perl
use strict;
use warnings;
use lib '../lib';
use Foo; # <-- comes from lib
But that means I have to be in the bin dir to run the script. Surely there is a better way. What's the Right Way to do this?

The standard FindBin module does what you want.
use FindBin;
use lib "$FindBin::Bin/../lib";
perldoc FindBin for more.

Parse out the complete path to your .pl via __FILE__ and and tack the ../lib on the end or pop off the last element of split(/\//,__FILE__) and add /lib to that.

I generally use this technique. Its sadly inspired from my PHP days:
Its handy in situations where you know where a given file will be relative to the current one, and aren't sure of the entry points it may be called in or the surrounding environment at calltime.
However, I would generally use this technique only for test scripts which need dummy libraries for emulating things.
use File::Basename ();
use Cwd ();
my $base_dir;
my $relative_path;
BEGIN {
$relative_path = '../../' # Path to base of project relative to the current file
$base_dir = Cwd::realpath( File::Basename::dirname(__FILE__) .'/' . $relative_path );
}
use lib "${base_dir}/lib";
use Foo;
Ideally there should be some module somewhere that does this, if not, I'm half tempted to write one:
use Some::Module ();
use lib Some::Module::relative_self('../../lib', __FILE__ );

The "FindBin" module will only work if the directory that the perl script resides in is in your system PATH, else it will fail. To overcome that you can manipulate the $0 value to get your path-to-perl-module information and pass the value to use lib.
Something like this -
BEGIN {
use File::Spec::Functions qw(rel2abs);
use File::Basename qw(dirname);
#Covert the script path to absolute and get its directory name
our $path = dirname( rel2abs($0) );
#Replace the bin tag with lib to get module directory
$path =~ s{bin/?$}{lib};
}
use lib $path;
EDIT:
The FindBin module works just perfectly and can be used as described in Michael's answer. My understanding of its workings was incomplete and so led me to making the first comment which I now retract. Anyway, I don't see any reason why this method shouldn't work albeit with a few more lines than could be achieved using FindBin (TMTOWTDI).

use lib './';
has been working for me with Perl v5.14.2 on a linux box to include files in the same directory as my script.
Perhaps it would work for you to move your lib and bin directories under your script's working directory and try to reference them using
use lib './bin/';

My solution:
use lib substr(__FILE__, 0, rindex (__FILE__, "/"));

Just to add my own two cents to this collection of answers, I usually solve this problem using something along these lines:
use lib do {
use Cwd 'realpath';
my ($dir) = __FILE__ =~ m{^(.*)/}; # $dir = path of current file
realpath("$dir/../lib"); # path of '../lib' relative to $dir
};
I like using a do block because everything needed is so neatly contained therein. If you ever need to copy/paste, or try to understand your code at a later time you don't have to look for a separate BEGIN block or anything like that.
The above code naïvely assumes that / is used as a dir/filename separator.

How about:
BEGIN: {
push #INC, '/full/path/to/lib';
}
To do a relative reference would assume that you're going to keep it in the bin dir, so insert the relative reference there instead.

Related

Perl: Use custom nested modules organised in nested sub-directories

I'm trying to figure out how to use nested modules organised in nested sub-directories with Perl. I mean, a test.pl program use a Foo module, and this in turn use another Bar module and so on... Let's put a little example, files could be organised in directories like that:
./test.pl
./lib/Foo.pm
./lib/common/Bar.pm
First thing that comes to mind is using the FindBin module in test.pl like that:
use FindBin;
use lib "$FindBin::RealBin/lib/.";
use Foo;
But if you want to do the same inside Foo to "use Bar", you need to include all the relative path from the test.pl program, including "/lib" segment. That means Foo needs to be aware of his relative path to the calling program. This forces kind of rigidity in the directories structure. For example, you can't simply copy&paste your custom modules wherever you want and call it. Besides, it needs to install the FindBin module in order to work.
In order to solve this issues, googling I found this solution: BEGIN solution that directly add paths to #INC. With this in mind, a solution could be:
./test.pl
#!/usr/bin/perl
use strict;
use warnings;
# Include lib in #INC
BEGIN {
use File::Basename;
my($filename, $dirs, $suffix) = fileparse(__FILE__);
my $common_path = $dirs."lib/.";
unshift(#main::INC, $common_path) ;
}
use Foo;
print "Inside: $Foo::message";
./lib/Foo.pm
package Foo;
# Include Common library
BEGIN {
use File::Basename;
my($filename, $dirs, $suffix) = fileparse(__FILE__);
$common_path = $dirs."common/.";
unshift(#main::INC, $common_path) ;
}
use Bar;
$message = " Foo > $Bar::message";
1;
./lib/common/Bar.pm
package Bar;
$message = "Bar";
1;
Executing ./test.pl should print:
Inside: Foo > Bar
You could nest any number of modules in their respective directories (I tested three levels), and what is better, copy&paste at whatever point of path without breaking functionality.
Nevertheless I dont't know if this method is advisable or have any disadvantages (I'm newby with perl). For example, is it advisable editing #INC directly like this?. is it advisable use the BEGIN code block for that?. Or, are there better ways to do this (allowing copy&paste directories at arbitrary points of the modules directories structure) so it works without touching code inside the modules?
Don't change #INC in modules. Leave that to the script.
use FindBin qw( $RealBin );
use lib "$RealBin/lib", "$RealBin/lib/common";
That means Foo needs to be aware of his relative path to the calling program.
If the libs are there for the script and each other, that's not a problem, and using use lib makes sense.
Otherwise, it's just a module for any script to use, and use lib shouldn't be used. Instead, it should be installed into standard installation directory (or a custom one specified by env var PERL5LIB).
By the way, you probably shouldn't put common inside of lib. This implies you can do use common::Bar;, which would be wrong.

How to test your own Perl modules while not depending on other modules in production?

First of all, I think somebody needs to rewrite my question, I know what I am asking, not how I should ask it precisely.
Assume I have some local module LOCAL::Commons and it has one subroutine called globalTrim. I am testing that subroutine below, and while LOCAL::Commons is installed under /usr/local/lib/perl, the module I am testing below is located in directory /home/me/perl/LOCAL/. See how I am using use lib ... to make sure I am not using LOCAL::Commons located in /usr/local/lib/perl (I have this directory on my path).
Now, if LOCAL::Commons is using another local module LOCAL::Cool (that is, not from cpan), and I have also made some changes to that module, how can I make sure my tests are using the correct modules? That is, I want LOCAL::Commons to use /home/me/perl/LOCAL/Cool and not /usr/local/lib/perl/LOCAL/Cool.
#!/usr/bin/perl
# test_local_commons.pl
# directory: /home/me/perl
use strict;
use warnings;
use Test::More 'no_plan';
use File::Temp qw( tempfile tempdir );
use Cwd qw();
use lib Cwd::abs_path();
# Testing
use LOCAL::Commons qw ( globalTrim );
sub newTest($) {
my $name = shift;
print "---------------------------------------------------\n";
print $name, "\n";
print "---------------------------------------------------\n";
}
sub testTraverse {
is(globalTrim(" - stackoverflow - ), "-stackoverflow-", "Passed" );
}
newTest "testTraverse"; testTraverse ();
If you run this like so:
perl -I/home/me/perl test_local_commons.pl
It should ensure your /home/me/perl version is checked first
After you load the files you want, put this:
die 'Included PRODUCTION Module!!!'
if grep { m{/usr/local/lib/perl/LOCAL/Cool} } values %INC
;
Of course you could die for any module you wanted.
For example, we've got this tool at work that works with Activestate's PerlEZ.dll. We don't want to deploy it with Perl installed. But we need to use some libraries, which we hide elsewhere. When I'm testing my code I include a module from our hidden path. It blows up anytime it sees a standard library path in %INC. We want to make sure it almost everything from the #INC hook and the special libraries.
I suggest using Perlbrew to make a completely separate Perl installation for testing. That way you control the testing environment and don't really have to worry about it.
use lib has process scope -- that is, it changes the value #main::INC with global visibility, just as a non-localized assignment to #INC would. That means that any modules that you load after saying use lib will check the include path you've set.
As to what path use lib sets, it explicitly adds to the front of #INC so that later calls to use lib will be considered earlier when searching for modules.
The upshot of which is that it looks to me like your code sample will just work. Note that I'm going to discourage you from checking that you're using the under-development version in your test. That would cause your test to fail based on something unrelated to whether the function under test actually works correctly. (But note also that you should also have written unit tests for LOCAL::Cool.)
You could just add a BEGIN{ #INC = qw(directories you want to allow)} block. While using PERL5LIB, use lib etc. just appends to the include path, this will completely replace it, thus eliminating the danger that the module is picked up later in the search path, e.g. because you forgot to install it /home/me/perl/.
But the real TDD answer is probably to use mock modules for everything that's not the module under test.

How do I return absolute path names for files using Perl glob?

I have some Perl code which has file glob operation.
$file1 = #ARGV[0];
#res1 = glob "$file1*";
I want the whole absolute paths to be reflected when i glob the files, not just the file names which is the case currently in my code.
e.g. when I do glob "*.pdf" i need the absolute paths of the pdf files to be matched and returned to my array variable by glob.
I tried using module use File::Basename;
but that seems to be used for parsing a file path into directory, file name , suffix.
How do I get this effect.
thanks,
-AD.
You want to use the core module Cwd to get the full path with respect to your current working directory.
use Cwd;
#res1 = map { Cwd::abs_path($_) } glob "$file1*";
You want to use the standard module File::Spec. It has a sub, rel2abs() which is exactly what you want. See perldoc File::Spec for details. Also, see perldoc perlmodlib for the list of standard modules and pragmatics that are install along with Perl.

How can I "use lib" the appropriate directory depending on installation location?

I have an object-oriented web-app that is installed in multiple locations on my server. Once for "live", once for "beta", etc. Being object-oriented, it consists of many perl modules. In the main module, I must "use lib" the appropriate directory for all of the custom perl modules for that instance of the app.
This is no big deal, I have a BEGIN block that checks the location of the main program and sets the library directory appropriately. However I also have a lot of utility, command line programs that need to do the same thing. I don't want to cut and paste this code everywhere.
What is the best way to share this code snippet amongst the various programs that need it?
I can't "use" it because the libary path isn't set up yet. Maybe "do" or "require" would the be the right answer, but both of those will search #INC, which is inappropriate.
Maybe something like eval `cat GetLib.pl`; would be appropriate but it seems kind of clunky and fragile.
Here is the BEGIN block that I currently use:
BEGIN {
use FindBin qw ($Bin);
require lib;
if ($Bin =~ /^\/home\/w\/myapp_live/) {
lib->import('/home/w/myapp_live/lib');
print STDERR "live site\n";
}
if ($Bin =~ /^\/home\/w\/myapp_beta/) {
lib->import('/home/w/myapp_beta/lib');
print STDERR "beta site\n";
}
if ($Bin =~ /^\/home\/w\/myapp_test/) {
lib->import('/home/w/myapp_test/lib');
print STDERR "testing site\n";
}
}
Thank you!
FindBin::libs is excellent for that. I've used it for a while in a large system with no problems at all.
The default invocation looks like it'll work for you, simply:
use FindBin::libs;
This will search for all the ./lib dirs in all the parent directories of the current file's dir and use lib them. So, for example, if your script lives in /home/w/myapp_live/scripts/defurblise_widgets.pl (and use()es FindBin::libs), it will look for:
/home/w/myapp_live/scripts/lib
/home/w/myapp_live/lib
/home/w/lib
/home/lib
/lib # (presumably!)
Any that it finds with be added to you #INC with use lib.
But, if that's not quite what you need, it's a very flexible module. I'd be surprised if you can't find a way to make it do what you want.
If you're running a program from the command line, don't programmatically set the lib: just pass it in as an argument, e.g.: perl -I/location/of/my/lib myprog.pl.
For your web app, why don't you make your library relative to the location of the script itself? Then just install it on each machine where the libraries live.
use FindBin;
use File::Spec::Functions;
use Cwd qw(abs_path getcwd);
BEGIN {
my $curdir = getcwd;
my $selfdir = $FindBin::Bin;
my $libdir = abs_path(catdir($selfdir, 'lib'));
chdir $libdir or die "can't chdir to $libdir: $#";
use lib $libdir;
}
Of course, the easiest option of all is to not use different lib directories. Why can't you be consistent across all your environments?
Edit. Re your comment "The reason I have to use different lib directories is because the code running in the live site is different than the code running on the beta site... that's the point of having a beta site." -- why don't you handle this at the level of the installer, rather than making the code itself have to know whether it's live vs. beta? e.g. store your code in different directories in your source tree as you do now, but only install the relevant code to each box. After all, that's exactly what a good revision control system would do for you -- you only check out one branch at a time, and you should only be installing one version of code at a time (as brian d foy alluded to).
I use the following in many of my scripts:
use strict;
use warnings;
use 5.008;
use FindBin;
use lib $FindBin::Bin;
That last line could be modified as such:
use FindBin;
use lib "$FindBin::Bin/lib";
The different environments could have different settings for the environment variable $PERL5LIB.
The code you've shown looks reasonable. You could install one copy of that code into a system-wide location, and then the code to invoke it would boil down to
require '/path/to/findlib.pl';
findlib->import;
The form of require that takes a filename doesn't search #INC.
As an alternative if you wanted to change lots of things around you could look into deploying the app in a way that would be more friendly to local::lib usage.

Is it mandatory that a folder by the name of a package should be present for creating a package?

We are factoring out the common code from our Perl project. One main program should be split into several re-usable modules.
Our program name is validate_results.pl which contains set of validation commands. We are planning to split this into small modules so that validate_results.pl should be like:
use Common::Validate_Results;
use Common::Validate_Results::CommonCommands;
use Common::Validate_Results::ReturnCodeValidation;
...
As per my understanding I should create a Common folder and under that Validate_Results.pm should be present. Again under Common, Validate_Results folder should be created and under that CommonCommands and ReturnCodeValidation folders should be present.
Is it mandatory that all these folders should be present or can we have all the Perl programs in a single folder and logically group them and still use the above way to access the modules (say use common::validate_results like that).
The filesystem hierarchy is required. A::B::C will always be located in A/B/C.pm, somewhere in #INC.
If you have to get around this, read perldoc -f require, specifically looking for the section about subroutine references in #INC. Yes, you can make the module loader do weird things if that's what you really want; but that's not what you want, trust me. Just stick to the convention, like the other 99.9999999% of Perl applications do.
If you want to 'use' your modules, then you must conform to the structure. If you want to get around that you can 'require' your modules instead, passing the filename to require.
You really shouldn't do this, though. If you truly don't want to have a directory structure, take it out of the module names (though that can lead to problems in the future if you ever have a module name that conflicts with something more generic from CPAN). Simply add the scripts directory to the INC path via Find::Bin and use the modules directly:
use FindBin;
use lib $FindBin::Bin;
use ValidateResults;
use CommonCommands;
use ReturnCodeValidation;
HTH
Here's an example of a module and it's sub-modules in the same file:
package Foo;
use strict;
use Exporter 'import';
our #EXPORT = ( 'from_foo' );
sub from_foo { print "from_foo\n"; }
package Foo::Bar;
use strict;
use Exporter 'import';
our #EXPORT = ( 'from_foo_bar' );
sub from_foo_bar { print "from_foo_bar\n"; }
1;
In your program, if you use module Foo (the one with a .pm file):
use Foo;
You will have access to Foo::Bar functions, except only as canonical names (Foo::Bar::from_foo_bar). You can import them like this:
use Foo;
Foo::Bar->import;
Note that you can't do this:
use Foo::Bar;
Because there is no file Foo/Bar.pm.
The package name in a 'use' command is effectively just a path which ends with a .pm file, so you don't need a folder with the name of every package. In your example, you need folders:
Common
Common/Validate_Results
But you don't need folders:
Common/Validate_Results/CommonCommands
Common/Validate_Results/ReturnCodeValidation
The actual package name in the .pm file does not have to be the same as the name in the 'use' command that loads it. But keeping the paths consistent with the package names is always a good idea.