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

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.

Related

In Perl, why do I get "undefined subroutine" in a perl module but not in main ?

I'm getting an "undefined subroutine" for sub2 in the code below but not for sub1.
This is the perl script (try.pl)...
#!/usr/bin/env perl
use strict;
use IO::CaptureOutput qw(capture_exec_combined);
use FindBin qw($Bin);
use lib "$Bin";
use try_common;
print "Running try.pl\n";
sub1("echo \"in sub1\"");
sub2("echo \"in sub2\"");
exit;
sub sub1 {
(my $cmd) = #_;
print "Executing... \"${cmd}\"\n";
my ($stdouterr, $success, $exit_code) = capture_exec_combined($cmd);
print "${stdouterr}\n";
return;
}
This is try_common.pm...
#! /usr/bin/env perl
use strict;
use IO::CaptureOutput qw(capture_exec_combined);
package try_common;
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(
sub2
);
sub sub2 {
(my $cmd) = #_;
print "Executing... \"${cmd}\"\n";
my ($stdouterr, $success, $exit_code) = capture_exec_combined($cmd);
print "${stdouterr}\n";
return;
}
1;
When I run try.pl I get...
% ./try.pl
Running try.pl
Executing... "echo "in sub1""
in sub1
Executing... "echo "in sub2""
Undefined subroutine &try_common::capture_exec_combined called at
/home/me/PERL/try_common.pm line 20.
This looks like some kind of scoping issue because if I cut/paste the "use IO::CaptureOutput qw(capture_exec_combined);" as the first line of sub2, it works. This is not necessary in the try.pl (it runs sub1 OK), but a problem in the perl module. Hmmmm....
Thanks in Advance for any help!
You imported capture_exec_combined by the use clause before declaring the package, so it was imported into the main package, not the try_common. Move the package declaration further up.
You should take a look at the perlmod document to understand how modules work. In short:
When you use package A (in Perl 5), you change the namespace of the following code to A, and all global symbol (e.g. subroutine) definitions after that point will go into that package. Subroutines inside a scope need not be exported and may be used preceded by their scope name: A::function. This you seem to have found.
Perl uses package as a way to create modules and split code in different files, but also as the basis for its object orientation features.
Most of the times, modules are handled by a special core module called Exporter. See Exporter. This module uses some variables to know what to do, like #EXPORT, #EXPORT_OK or #ISA. The first defines the names that should be exported by default when you include the module with use Module. The second defines the names that may be exported (but need to be mentioned with use Module qw(name1 name2). The last tells in an object oriented fashion what your module is. If you don't care about object orientation, your module typically "is a" Exporter.
Also, as stated in another answer, when you define a module, the package module declaration should be the first thing to be in the file so anything after it will be under that scope.
I hate when I make this mistake although I don't make it much anymore. There are two habits you can develop:
Most likely, make the entire file the package. The first lines will be the package statement and no other package statements show up in the file.
Or, use the new PACKAGE BLOCK syntax and put everything for that package inside the block. I do this for small classes that I might need only locally:
package Foo {
# everything including use statements go in this block
}
I think I figured it out. If, in the perl module, I prefix the "capture_exec_combined" with "::", it works.
Still, why isn't this needed in the main, try.pl ?

Exporting subroutines from a module 'used' via a 'require'

I'm working with a set of perl scripts which our build system is written with. Unfortunately they were not written as a set of modules, but instead a bunch of .pl files which 'require' each other.
After making some changes to a 'LogOutput.pl' which was used by almost every other file, I started to suffer from some issues caused by the file being 'require'd multiple times.
In an effort to fix this, while not changing every file (some of which are not under my direct control), I did the following:
-Move everything in LogOutput.pl to a new file LogOutput.pm, this one having everything needed to make it a module (based on reading http://www.perlmonks.org/?node_id=102347 ).
-Replace the existing LogOutput.pl with the following
BEGIN
{
use File::Spec;
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules');
}
use COMPANY::LogOutput qw(:DEFAULT);
1;
This works, except that I need to change calling code to prefix the sub names with the new package (i.e. COMPANY::LogOutput::OpenLog instead of just OpenLog)
Is there any way for me to export the new module's subroutine's from within LogOutput.pl?
The well named Import::Into can be used to export a module's symbols into another package.
use Import::Into;
# As if Some::Package did 'use COMPANY::LogOutput'
COMPANY::LogOutput->import::into("Some::Package");
However, this shouldn't be necessary. Since LogOutput.pl has no package, its code is in the package it was required from. use COMPANY::LogOutput will export into the package which required LogOutput.pl. Your code, as written, should work to emulate a bunch of functions in a .pl file.
Here's what I assume LogOutput.pl looked like (using the subroutine "pass" as a stand in for whatever subroutines you had in there)...
sub pass { print "pass called\n" }
1;
And what I assume LogOutput.pl and LogOutput.pm look like now...
# LogOutput.pl
BEGIN
{
use File::Spec;
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules');
}
use COMPANY::LogOutput qw(:DEFAULT);
1;
# LogOutput.pm
package test;
use strict;
use warnings;
use Exporter "import";
our #EXPORT_OK = qw(pass);
our %EXPORT_TAGS = (
':DEFAULT' => [qw(pass)],
);
sub pass { print "pass called\n" }
1;
Note this will not change the basic nature of require. A module will still only be required once, after that requiring it again is a no-op. So this will still not work...
{
package Foo;
require "test.pl"; # this one will work
pass();
}
{
package Bar;
require "test.pl"; # this is a no-op
pass();
}
You can make it work. Perl stores the list of what files have been required in %INC. If you delete and entry, Perl will load the file again. However, you have to be careful that all the code in the .pl file is ok with this. That #INC hack has to make sure its only run once.
BEGIN
{
use File::Spec;
# Only run this code once, no matter how many times this
# file is loaded.
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules')
if $LogOutput_pl::only_once++;
}
use COMPANY::LogOutput qw(:DEFAULT);
# Allow this to be required and functions imported more
# than once.
delete $INC{"LogOutput.pl"};
1;
This is one of the few cases that a global variable is justified. A lexical (my) variable must be declared and would be reset with each loading of the library. A global variable does not need to be declared and will persist between loading.
This turned out to just be a stupid mistake on my part, I didn't put the subs into the #EXPORT list, only into #EXPORT_OK.

using files other perl files in another perl file [duplicate]

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
How do I include functions from another file in my Perl script?
I have a perl file suppose a.pl and I want to use a function from perl file b.pl. How do i do it
Turn b.pl into a module
Call it something descriptive like MyBModule (B is reserved by core).
rename the file to the something .pm like MyBModule.pm.
Add a package at the top, like package MyBModule;
Set a true return code on the package by making the last line 1;
You don't have to do anything else if you want to use your package name when calling the sub.
use MyBModule;
use strict;
use warnings;
MyBModule::sub1();
If you don't want to qualify it with the package name, read on...
Use Exporter.pm
Now configure Exporter.
Add the use Exporter; statement at the top of your module.
add a line our #EXPORT_OK = qw(sub1 sub2);
After you're done your module should look something like this
package MyBModule;
use strict;
use warnings;
use Exporter;
our #EXPORT_OK = qw(sub1 sub2);
sub sub1 { ... }
sub sub2 { ... }
Edit the caller
Make sure the library is in #INC, or the module in the current directory. If not append the directory to PERL5LIB.
Add a line like use MyBModule qw(sub1 sub2);
Read perldoc Exporter for more information
Your script should look like this afterward:
use strict;
use warnings;
use MyModuleB qw( sub1 sub2 );
It really isn't that hard, it takes about 15 seconds after you get used to doing it.
The best/conventional way is to keep all your functions in a Perl module file (.pm): See perlmod. This would require you to convert b.pl to a package. You would then access your module file (MyFuncs.pm) from a.pl with:
use MyFuncs;
Use the require and/or use functions.
Use the 'use' keyword to use functions from another module.
Example:
File a.pl:
use b;
f_in_b();
File b.pm:
sub f_in_b()
{
print "f_in_b\n";
}
1;
Important:
File b.pm must have the final 1;

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

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.

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.