perl - package name: Can I use path from root? - perl

I am reading documentation about use. It is the same as
BEGIN{ require Namespace::NameOfModule; }
And the perl interpreter loads the module and translate the double colon :: to system path's separator (UNIX / and WINDOWS \\, if I remember correctly). I just wondered, If I am able to load module from root. Because in that case, for example module DateTime.pm in dir /home/nickname/dir. It would be (according to double colons rule) ::home::nickname::dir, which is wrong (like it should even look like terrible package path). So how - if even - is possible to load a module from root dir? And Does the path starts from current dir by default? (That is from dir where is the perl script located in), or only from #INC dirs?

The module namespace is relative to #INC directories only. These are by default set up according to where Perl was installed, with privlib (core modules installed with Perl), sitelib (modules installed by a CPAN client), and vendorlib (modules installed by a vendor package manager), and architecture-specific versions of each of these. Additional #INC directories may be added by local::lib, and before Perl 5.26 #INC also included . (the current working directory), but this was a bad idea.
When you invoke use or require on a bareword package name, it does the translation you described (convert :: to path separator and append .pm), and then appends it to each directory in #INC until it finds a file. (It also checks for .pmc but this is rarely relevant.) The package statement inside the file is expected to match the module path used to look it up for the import component of use.
You can modify #INC manually, but it is best to do it with one of the following mechanisms, so that architecture-specific and version-specific subdirectories will be respected if present.
lib - use lib '/path/to/lib';
-I - perl -I/path/to/lib ...
PERL5LIB - env PERL5LIB=/path/to/lib perl ...
These options should only be used to insert absolute paths into #INC, since relative paths will have the same vulnerability as . used to, in that they may mean something different when the current directory is changed. To add a path relative to the script wherever it may be, lib::relative can be installed to simplify the process, or its documentation describes equivalent functionality using core modules.
use lib::relative '../lib';
Once you have added your custom location(s) to #INC, that directory will be searched first for any further use calls.
use Foo::Bar; # now checks /path/to/lib/Foo/Bar.pm first
# which should contain package Foo::Bar

Even if you could load the module like that, you'd have numerous other problems.
The module's package directive would have to be package ::home::nickname::dir::SomeModule; for import to be found.
You'd have to use ::home::nickname::dir::SomeModule->some_method to call a static method.
You'd have to use ::home::nickname::dir::SomeModule::some_sub to call a sub.
That's obviously the wrong approach.
For modules installed in a position relative to a script, use the following in the script to tell perl where to look:
use FindBin qw( $RealBin );
use lib "$RealBin/../lib"; # Or whatever.
For modules installed for any script to use, use the following environment variable:
export PERL5LIB="$HOME/dir" # Or whatever.

Related

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.

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 is use local::lib different from use lib?

I don't understand what use local::lib does that regular use lib doesn't. Could someone explain it?
local::lib
Defaults to ~/perl5 if you don't specify a directory (while use lib; is a no-op).
Resolves relative paths to absolute paths before adding them to #INC. (lib just adds the relative path as-is.)
Expands ~ and ~user in the directory name.
Appends /lib/perl5 to the directory you specify. (So use local::lib '/foo'; is somewhat equivalent to use lib '/foo/lib/perl5';.)
Prepends DIR/bin to your PATH, so you can use scripts installed by local modules.
use lib adds a directory to your module search path (#INC). It has no effect on anything outside of the program or module which contains the use lib directive.
local::lib is intended to be used to enable a private module installation directory and, if you configure your shell environment in the way that it recommends, this private directory will be used for all Perl module installations (whether via CPAN or manual make install) and modules installed there will be made available to all Perl programs/modules run from within your local::lib-aware shell environment.
Regular use lib foo is almost as simple as:
BEGIN { unshift(#INC, foo) }
Whereas use local::lib sets many other Perl environment variables to make sure you can install modules locally, see the source.

Where do I install Perl modules that I wrote?

I wrote some modules, and trying to run them. How I tell where to take the module from?
I am writing in Windows, and tried to put it in c:\perl\lib but it didn't help.
Can I put it in the same directory, as the file that calls the module?
Perl uses the paths in:
#INC
to search for modules. (.pm files)
If you:
perl -e 'print join "\n", #INC;'
you'll see what paths are currently being searched for modules. (This #INC list is compiled into the perl executable)
Then you can:
BEGIN { unshift #INC, 'C:\mylibs' }
or
use lib 'C:\mylibs'
and place MyModule.pm inside C:\mylibs to enable:
use MyModule;
This is exactly what local::lib is designed to handle. By default, use local::lib; will prepend ~/perl5 to your module search path (#INC), but you can easily tell it to add another directory.
If you're doing it with a fixed directory (rather than one relative to your home directory), then you're probably just as well off with use lib 'PATH'.
If this is for code that will only be run from the command line, another option would be to create a PERL5LIB environment variable which points to your personal module directory. This would affect all Perl run by your user account from the command line, so you wouldn't need to modify your Perl code at all with this method, but it's trickier to set up for non-command-line code (e.g., web scripts) and, if the code will be run by multiple users, PERL5LIB will need to be set for all of them.
I wouldn't recommend mucking about with #INC directly in any case. There are plenty of easier ways to do it these days.
For application-specific libraries the common approach is to use the FindBin module to locate the application directory and then use lib to add the application's library to #INC:
use FindBin;
use lib "$FindBin::Bin/lib";
use AppModule;
For general-purpose modules, I recommend developing them the same way you would for a CPAN release (e.g. start with module-starter) and install them with perl (usually under site/lib). See also: Which framework should I use to write modules?
If you can't install with perl (i.e. you don't have the necessary permissions) you can have a personal library instead. See How do I keep my own module/library directory? in perlfaq8.

How can my Perl script find its module in the same directory?

I recently wrote a new Perl script to kill processes based on either process name / user name and extended it using Classes so that I could reuse the process code in other programs. My current layout is -
/home/mutew/src/prod/pskill <-- Perl script
/home/mutew/src/prod/Process.pm <-- Package to handle process descriptions
I added ~/src/prod in my $PATH variable to access the script from anywhere. On running the script from any directory other than its resident directory leads to a "Can't locate Process.pm in #INC" (which is understandable given that other than the shared directories in /usr, #INC includes only the current directory - '.'). One workaround that I have been using is the use lib directive as so -
use lib '/home/mutew/src/prod';
but this is a major portability issue. Any solutions which will also allow me to export the script to other systems without and changes?
EDIT
I chose 'depesz' answer as the correct one because of its simplicity and core module usage.
brian d foy's answer though suggests other methods to accomplish the same (TMTOWTDI), his contribution in perlfaq8 renders this question absolutely redundant.
I am curious why the simplistic solution
use File::Basename;
use lib dirname (__FILE__);
use SomeModuleLocatedInTheSameDirectoryAsThisPerlScriptOrModule;
did not come up. The FindBin module seems to have some issues if the file is not the main executable perl script, but simply a non-executable Perl module. At least that's how I interpret the comment in the documentation. Did not really test it.
To have any other path relative to the location of this Perl file, do something like
use File::Basename;
use lib dirname (__FILE__) . "/MyModules";
The simplest approach I found it to use FindBin module. Like this:
use FindBin;
use lib $FindBin::Bin;
Generally I prefer to have my scripts provided in such a way that programs are in whatever/bin, and libraries are in whatever/lib
In these situations I use a slightly more complicated approach:
use Cwd qw(abs_path);
use FindBin;
use lib abs_path("$FindBin::Bin/../lib");
The abs_path call is to make the #INC contain whatever/lib, and not whatever/bin/../lib - it's just a slight change, but makes reading error messages easier.
From perlfaq8, which answers "How do I add a directory to my include path (#INC) at runtime?". There are several other answers for questions around this issue too.
How do I add a directory to my include path (#INC) at runtime?
Here are the suggested ways of modifying your include path, including environment variables, run-time switches, and in-code statements:
the PERLLIB environment variable
$ export PERLLIB=/path/to/my/dir
$ perl program.pl
the PERL5LIB environment variable
$ export PERL5LIB=/path/to/my/dir
$ perl program.pl
the perl -Idir command line flag
$ perl -I/path/to/my/dir program.pl
the use lib pragma:
use lib "$ENV{HOME}/myown_perllib";
The last is particularly useful because it knows about machine dependent architectures. The lib.pm pragmatic module was first included with the 5.002 release of Perl.
Besides the already stated solutions:
use FindBin / lib
Perl Faq 8 How do I add a directory to my include path (#INC) at runtime?
'The simplest approach' (™) that I use while dev/testing a module prior
to deploying it (in /usr/local/lib/site_perl/ or elsewhere in #INC) is
to modify #INC before loading the module as follows:
#!/usr/bin/perl
use strict;
use warnings;
# Modify #INC prior to module loading.
BEGIN { unshift #INC, '.'; }
use YourModuleInCWD;
(Add current working directory to #INC? - PerlMonks)
You can make perl look in any directory by using the -I flag. Here, -I stands for #INC which is the array of paths in which perl looks for modules. Using -I adds the given path to the #INC array for this execution.
eg:
perl -I lib bin/script.pl
where lib contains the modules I want to use.
I know that this works for perl 5. I am not at sure about other versions.
Take a look at Par::Packer. It creates an executable with all of the script's dependencies included. It makes distribution easy. You also could also provide your users a version of your module that can be installed on their systems. See Module::Starter for an easy way to build all of the files required to make a standard CPAN-style distribution.
The path can be included in perl script in first line, marked with #!
#!/usr/bin/perl -I.
use anymodule;
To convert relative links to absolute and add to #INC, I normally use ex::lib. Of course, this is useful only if you plan to keep the relative locations of the script and the module identical on all machines where you plan to run your script.
Just keep it simple. There is no need to import any libraries; just find out your current working directory:
use lib "$ENV{PWD}/relativ_path_own_perllib";
For similar problems, you can read out the environment variable which gives your information about where you are, your home directory, operating system stuff, and so on, with just one row of programming code in the shell-terminal, like:
perl -e 'map { print; print " : ". $ENV{$_}." \n\r"; } sort keys %ENV '
There is no need to bind some libraries; just use the %ENV-Hash.
FindBin::libs will find all your libs placed at reasonable places relative to the path from where your script is running.