How can a perl module find its own path? - perl

I'm creating my own module, lets call it X::Y. Of course, the module will be in the file X/Y.pm.
Lets say Y needs to call an external program, prog. Ideally I'd just like to put prog in X, so I can run X/prog. I'd like to not have hardcode X/progs full path, and for the module to work regardless of the current working directory set.
How can I find the full path of a module from inside a module? Or is there a better way to do this?

The full path to the source file currently being executed is supplied by Perl's __FILE__ special literal.
However I would prefer to see the external program installed where it would normally be, and the path there either coded as a constant in the Perl code or included in the PATH environment variable.

Borodin answered the question but some related information:
FindBin - finds the directory that the script was run from (use within the script itself or within a package loaded by it)
Neil Bower's CPAN modules for getting a module's path - detailed review of modules for finding another module's path.

Once a module is loaded, it's path is in the global %INC variable. To look it up, you need to do a simple conversion:
change :: in the package name to /
append .pm
So to find the location of the module X::Y, you would look in $INC{"X/Y.pm"}.

Related

Calling a Perl module from another Perl program

I am analyzing an existing infrastructure of static analysis tool. I see there is a perl module/file called static_analysis.pm and also a directory with the name static_analysis. And I see other scripts such as run_sa having the statement use static_analysis. I am not able to understand whether use static_analysis is calling the perl module/file or the directory.
According to the docs, use Module imports some semantics into the current package from the named module, generally by aliasing certain subroutine or variable names into your package.
Thus, the statement use static_analysis imports from the file (module) static_analysis.pm, not from the directory static_analysis.

Include perl module from a different location

my problem is as follows
I have the lib in location ./abc/def/lib
I am using it in path ./abc/xyz/lib/tuv/abc.pm
I am running the code from a different location altogether
which is something like ./abc/xyz/blah1/blah2/../../ . I'm running the code here.
I have created env params
setenv def ./abc/def
setenv xyz ./abc/xyz
I tried the below solutions
Begin { push(#INC, $ENV{def}."/lib" ) }
Along with the providing the path for other libraries i used
use lib $ENV{xyz}."/lib"
This solution didn't work. I get the error saying the library in ./abc/def/lib
is not included.
I tried the FindBin
# for the libs present in ./abc/def/lib
use FindBin;
use lib "$FindBin::Bin/../../";
use mylib::abc;
use mylib::def;
(I tried giving the path also of root from where to start looking..in FindBin)
I tried using different combinations but not sure why it doesn't work.
Can someone point out what i am doing wrong or is my understanding of FindBin wrong.
You don't say what problems you're having. Just saying you're "not sure why it doesn't work" isn't really helpful. I assume you're getting a "Can't locate [some module] in #INC" error.
There are three (recommended) ways to change the value of #INC. Each have their uses.
Inside a program, you can use use lib
From the command line (or in a shebang), you can use -I
In your shell environment, you can set PERL5LIB
In all these cases, the new directories will be added to the start of #INC, so these directories will be searched before the standard library directories.
You can also manually manipulate #INC. Note that in order for manual manipulation of #INC to have any effect on use statements in your code, they will need to be executed in a BEGIN block (as use statements are executed at compile time). It looks like you have tried that too, but that you used Begin rather than BEGIN - which wouldn't work.
If you are running your program from a location that is completely unrelated to the directories where the libraries are kept, then FindBin is likely to be of limited use. FindBin is useful where the location of the executable file and the libraries are closely related (for example the executable file is in ./bin and the libraries is in ./lib).
It also seems that you are trying to adjust #INC from within a library (./abc/xyz/lib/tuv/abc.pm). Note that FindBin will not set $FindBin::Bin to this directory. $FindBin::Bin is always set to the directory that contains the executable file which is running.
Without knowing more about the actual paths that you are using (you have only given relative paths in your examples) it's hard to know for sure what the best approach is. But I think that in your situation I'd be setting PERL5LIB to the absolute path of your extra libraries.
The FindBin module is the best fit for your needs. The important thing to remember is that $FindBin::Bin refers to the directory that contains the script. So when you refer to it ...
use lib $FindBin::Bin . '/../../etc';
the '/../..' path must describe the path from the script directory to the lib directory.
The other important thing to take into consideration is the error message. It will start with Can't locate Your/Module/Name.pm but will then go on to list all the directories in #INC - the library search path. Check this list of directories to make sure it includes the lib path you intended to add.

import/require/use for perl modules

I have 2 files perl files which have dependency on one another.
The 1st file, A is a .pl file which multiple package/modules declared inside.
The 2nd file, B will try to access one of the many packages declared in file A. How can that be done?
You may be familiar with how Java or a similar language finds an import com.example.AppName namespace by looking for the com/example/AppName.java file in the classpath. When you ask Perl for use HTML::Template, it likewise looks for HTML/Template.pm in the directories listed in #INC.
The difference is that a Perl file can have multiple packages. When you say use HTML::Template, you'll be pulling in all packages listed in HTML/Template.pm (use automatically translates the :: into the right directory separator for your system, and adds the .pm). If there happens to be a HTML::Template::Extension package in that file, then you can use that package without having to explicitly use it. It's already loaded the complete file, and that's good enough.
If you know the exact file name relative where you'll be running the script, the easiest way to grab it is:
require 'path/to/file.pl';
You need to declare a module in each pl file that is using a function of the module.

How do I best use a library whose directory may change occasionally?

I am writing Perl scripts and when I have too many functions, I usually move them all into a library (also good for code reuse). So I usually create a package (e.g. my_lib.pm) and add use lib 'path/to/lib'; use my_lib; to my script.
I wonder if it's possible to skip the use lib 'path/to/lib';, which sometimes gives me trouble since I reorganize my directory hierarchy, and make Perl look for packages in the same dir where the script is running from.
Thank you.
First, i suggest you - "Never mess up with Core Perl and its libraries - never put your lib in among there".
If you want your script look into current dir, then use like:
require "mylibrary/functions.pm";
where mylibrary is a dir that exists the same path as your caller script.
I would put my .pm file into one directory so you can use if from Perl scrips irrespective of their location.
Then create an envrironment variable PERL5LIB with the name of that directory.
You need
use lib '.';

How can I force Perl to run the module from the test directory only?

I have perl module file and test script pl which I use to run some function, and after browsing through the use lib and this previous question here...
I still don't know how to do this.
Basically I have a production directory which contains the module file, and I have a test directory file which contains the same module and the test script file. each time I try to run the script file, it will automatically calls the module in the directory. by printing out the #INC, it seems that the production directory is hosted in there. thus I try to remove it by using
no lib qw(prod_dir);
and while printing out the #INC shows that the directory is no longer there, somehow the script is still calling that other module...
I know this probably sounds really dumb, but hope someone can help me see the light here :)
thanks.
After you have required or used the module, check %INC to see where it came from.
For example:
use Data::Dumper;
print $INC{'Data/Dumper.pm'}."\n";
Note that "::" becomes "/" and you append ".pm". That might give you a clue.
Remember that the current directory (".") is usually an entry in #INC. But the first step is finding out what directory the module was loaded from.
Another thing to remember is that the directories in #INC are searched in order. use lib prepends to that list (making it the first-searched directory), so you may just need to add the appropriate directory.
Can you say more about what you are trying to do and how you are trying to do it? Is this stuff in a standard Perl distribution structure? If you aren't using the standard distribution structure, can you show us a directory listing so we know where things are? Can you also include the code you use to try to load the module? Just update your original question when you pull together the details.
Typically, I run tests through the build runner, which automatically sets up the right #INC.
If I want to run one test in my distribution, I use the blib module to find the build library which has the development versions of my modules:
% perl -Mblib t/test.t
Some people do the same thing with prove.
If you aren't using the basic distribution set-up, consider using it. The tools and best techniques rely on it.
If you just have your module and test file in the same directory, have you tried adding the current directory to #INC with PERL5LIB?