So, my code (Perl scripts and Perl modules) sits in a tree like this:
trunk/
util/
process/
scripts/
The 'util' directory has, well, utilities, that things in the 'process/' dir need. They get access like this:
use FindBin;
use lib "$FindBin::Bin/../util";
use UtilityModule qw(all);
That construct doesn't care where you start, as long as you're at the same level in the tree as "util/".
But I decided that 'scripts/' was getting too crowded, so I created scripts/scripts1
scripts/scripts2
Now I see that this doesn't work. If I run a script 'trunk/scripts/scripts1/call_script.pl', and it calls '/trunk/process/process_script.pl', then 'process_script.pl' will fail trying to get the routines from UtilityModule(), because the p开发者_开发问答ath that FindBin returns is the path of the top-level calling script.
The first ten ways I thought of to solve this all involved something like:
use lib $path_that_came_from_elsewhere;
but that seems to be something Perl doesn't like to do, except via that FindBin trick.
I tried some things involving BEGIN{} blocks, but i don't really know what I'm doing there, and will likely just end up refactoring. But if someone has some clever insight into this type of problem, this would be a good chance to earn some points!
Have you considered using lib::abs? One feature of $FindBin
(which can be a limitation in some cases like yours) is that it works relative to the executed binary rather than relative to the caller. lib::abs on the other hand works relative to the module it is placed in. In addition you can also use globs like:
use lib::abs qw{../modules/*/somelib};
use
is a BEGIN block in disguise, so anything it references has to be defined at BEGIN time as well. This is the case for the variables in module FindBin, but not necessarily for other variables.
# this will fail: $path is not yet defined at BEGIN time
my $path = '../util';
use lib $path;
# this ought to work
my $path;
BEGIN { $path = '../util' }
use lib $path;
As to making FindBin return the right directory for both scripts in /scripts and /scripts/scripts[12], that seems tricky to me. And your question is still a bit vague on the subject. In my opinion, you should have all scripts in a scripts/scriptN directory; this way ../../util always resolves to the right place. But I'm not sure this applies to your case.
Without tailoring to your exact needs, I use this snippet (source <--last comment) often to use local libraries without hard coding. You probably could do the tailoring without too much work.
use Cwd 'abs_path';
use File::Basename;
use lib dirname( abs_path $0 );
NB: from perldoc perlvar
$0
Contains the name of the program being executed.
If your possible trunk locations don't contain "scripts" anywhere in their paths (e.g. no "/usr/scripts/stuff/trunk/scripts/" case exists, you can use the approach outlined here:
How do I use beta Perl modules from beta Perl scripts?
It basically involves a special library which analyzes the script's path, and modifies @INC
to a location that's absolute to that path (e.g. in your case, it finds "/prefix/scripts/" directory and adds "/prefix/scripts/../utils/" to @INC
even though the script is not in "/prefix/scripts/" but in "/prefix/scripts/scripts1"
Something quick and dirty along those lines if you don't feel like doing a generic library:
# Must be at the very beginning of your scripts
use Cwd 'abs_path';
BEGIN {
my $full_path_script_name = abs_path($0);
if ($full_path_script_name =~ !^(.+)/scripts/!) {
my $prefix = $1;
unshift(@INC, "$prefix/../util");
}
}
I usually use a shell script prefix command (usually called toolchain) to manipulate the running environments PERL5LIB.
- project/
- lib/
- util/toolchain
- bin/application
toolchain:
#!/bin/sh
export PERL5LIB=$PWD/lib:$PERL5LIB
export PATH=$PWD/util:$PWD/bin:$PATH
exec $*
The limitation with this script is that you must always call it from the project directory.
Call:
% ./util/toolchain application
What this does is it eliminated unnecessary use lib path
from the libraries and executable scripts. It leaves it up to the running environment (where it should be). It also makes the scripts and libs easily relocatable for production installation purposes.
If you are willing to rename util
to lib
, then you can use the perl package FindBin::libs
. See
http://metacpan.org/pod/FindBin::libs
Then you can simply say
use FindBin::libs;
use UtilityModule qw(all);
For each of the script's parent's directory, it will search for a lib
subdirectory and use it as the location of your modules.
In your case, your scripts in trunk/scripts/scripts1
will check for trunk/scripts/lib
and then trunk/lib
for your modules.
精彩评论