Calling a Perl module from another Perl program - perl

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.

Related

Checking if imported modules are ever used

For whatever reason my coworkers (and probably myself in the past) have imported modules that we don't use to our projects. It's so bad that modules that don't even exist are being imported.
In Perl, you can say something like:
use Socket;
gethostname("DERP");
It's very hard to tell from that function that it was tied to the socket module.
Is there any program that exists or some functionality of Perl that I'm not aware of that can tell you if you've got bloat imported into your script?
That's why I always explicitly lists imports. e.g. use Socket qw( gethostname ); or use Foo qw( ); (The latter imports nothing rather than defaults.)
One approach would be to go file by file and add these import lists, then stop loading module with an empty import list for which no mention of its package name exists in the file.
Use a profiler to see what modules ARE in use. From that find out which are not. Works for me:
Install the Devel::DProf module if you havent (cpanm Devel::DProf or if ubuntu: apt install libdevel-dprof-perl). Newer profilers also exists, i.e. Devel::NYTProf
Change #!/use/bin/perl to #!/use/bin/perl -d:DProf in your program and then run it. This will make a tmon.out file in your current dir
Run dprofpp -aO9999 to analyze tmon.out. This outputs all executed subs including the module name on each line.
Note: if a sub is called conditionally (inside an if-block for instance) the profiler will not list it if the condition is always false. It helps to have a test suit with full code coverage and run that in profiling mode.

How to dynamically add a program's library path to #INC in Perl program?

I'm working on an application that is written in Perl. The application is so large now that I want to move each class out into a separate file. This complicated building and installing the program; when it was only file it could easily be copied in place anywhere. Now I have to use a build system like Build::Module or ExtUtils::MakeMaker.
I have structured my source tree so that I have two directories: bin where the program launcher is, and lib where a number of modules are. Bin contains an executable Perl script which the user invokes, and it loads the necessary module from the lib directory.
The problem that I have is that I want the user to be able to specify a prefix where they want the program installed, similar to the --prefix option offered by packages based on GNU Autotools. Since this might not be a standard path where Perl looks for modules (for example /opt/program) the user will see a message saying something like Can't locate Program.pm in #INC.
Is there a way to make the program detect where the modules should be loaded frm and dynamically add that path to #INC? I don't want the user to have to manually work with environment variables like PERL5LIB in order to get the program running.
Couldn't you use findbin and lib ?:
use FindBin qw($Bin);
use lib "$Bin/lib";
If I understand you, you're breaking up a big large program into smaller components. Good for you! That's great programming technique. Making each class a true Perl module is a great idea. It makes your program so much easier to maintain.
I do this all of the time. First, I use the module name Local:: as my prefix. CPAN will never use Local as a module prefix, so I know I will never clash with some CPAN Module. Then, I put my Local module directory in the same directory as my script. In most standard Perl installations, when Perl searches #INC for modules, the last directory it searches for is the current directory (.). Since my module names will never clash with any CPAN modules, I know Perl will find my modules and only my modules under that ./Local directory.
You can now distribute the entire directory structure to other users. All a user has to do is install your entire directory (which includes the scripts and modules) and run the script. No need to go through an entire install process.
The following code works for me. You supply a command line option like (--prefix abc) and that value is appended to the lib path very early in your script. So all following modules will be searched with the dynamically set path.
use strict;
use warnings;
use Getopt::Long;
my $prefix = '';
BEGIN {
GetOptions ('prefix=s' => \$prefix );
$prefix = $prefix || '.';
}
use lib "$prefix";
use mymod; # uses the dynamical search path

How can a perl module find its own path?

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"}.

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.

Why doesn't my Perl script find my module even after I adjust #INC with FindBin?

I want to be able to use a module kept in the lib directory of my source code repository, and I want the only prerequisite for a developer to use the scripts that I'm writing is to have a standard Perl installation, but I'm not sure how to accomplish this.
In my scripts, I have
use FindBin qw($Bin);
use lib "$Bin/lib"; # store non standard modules here
use Term::ANSIColor;
use Win32::Console::ANSI;
print Term::ANSIColor::colored("this should be in color\n", "bold red");
and I put the module in ./lib. I verified that's the actual location where the module exists (by renaming it and causing it to fail). However, even if the module is in an arbitrary lib directory, it still seems to be a requirement that ppm be aware of the module.
I can not get my scripts to find/use it in lib without it being "installed" by ppm first. I would imagine that there should be some sort of way around this.
I know this may be an atypical request, but my goals are probably atypical. I just want a developer to do a checkout and immediately use some scripts without having to run some additional commands or use a package manager.
Thanks for any insight.
EDIT: I updated with a complete example. I also realized that if I uninstall it via ppm (but leave the pm in the referenced directory), I may have to change my syntax, and I was not considering that before. So maybe I have to give a full path or use require like jheddings or BipedalShark propose (ie. if it's not "installed", then I must use "require" and append ".pm" to it or use a BEGIN block.
If this is the case, then I have not found the correct syntax.
EDIT 2: Based on a comment below, I realize that I may have a flawed assumption. My reasoning is this: If I reference the actual code, the ".pm", directly then I should be able to use it without using a package manager. Maybe that's not the case, or if I want to do that maybe I have to do it a different way. Alternatively, I may have to refactor the code in the ".pm".
EDIT 3: I think that I was misunderstanding a few things. The error message in my IDE "Compilation failed in require", it's highlighting of the line that I was using to include the module, and the console error message of "Can't locate loadable object for module Win32::Console::ANSI"
I was reading that as a problem with loading the module itself, but it seems to be a problem that results from something the module itself is attempting to load. Interesting that this is only a problem since I didn't use ppm install though.
It is finding the actual module. I was able to verify that by commenting out the trouble lines.
Thanks for the help, but I'll have to spend some more time with it.
See perldoc perldiag under "Can't locate loadable object for module ...":
(F) The module you loaded is trying to load an external library,
like for example, "foo.so" or "bar.dll", but the DynaLoader module
was unable to locate this library. See DynaLoader.
You are correct that this problem is arising from something the module is trying to load -- that's what Dynaloader does. However, the documentation for Win32::Console::ANSI makes no mention of any external library requirements.
Are you preserving your module path structure in your lib directory?
i.e. your module should be in the path $Bin/lib/Some/Module.pm.
From perlfaq8's answer to How do I add the directory my program lives in to the module/library search path?
You appear to be doing it correctly, but you need to give us more if you expect to get help.
When you run that script, what ends up in #INC? Put in a debugging line like:
BEGIN {
use lib ...;
print "INC is \#INC\n";
}
Check that that output shows the directory that you expect. If it doesn't, start bisecting the problem from there.
Try this:
BEGIN {
use FindBin qw($Bin);
}
use lib "$Bin/lib"; # store non standard modules here
I manually install modules all the time and it seems to work. I just copy directories and files into a location and use the "use lib" directive like you've shown. Sometimes I miss a file and I get a runtime error that it's looking for a certain file and I go find the file on the Internet and put it in the right place and it works. Not sure what's going on with your setup. This should work.
I usually put the perl modules in the same directory as my script and then: use lib "."
But I don't know that it would matter.