v_melnik has asked for the wisdom of the Perl Monks concerning the following question:

Dear colleagues,

The script doesn't know how deep in the directory tree it lies: in '/***/monkeyman/bin' or in '/***/monkeyman/bin/test', but it needs to know it as it needs to load modules from /***/monkeyman/lib.

It seems it can't perform any regex matching before the compiler loads all libraries, am I right? Because, as I see, it doesn't...

use FindBin qw($Bin);
if($Bin =~ /^(\/.+\/monkeyman\/bin)(\/.+)?/) {
   use lib("$1/lib");
}
use MonkeyMan;

Leads to:

Use of uninitialized value $1 in concatenation (.) or string at ./someutil.pl line 9.
Can't locate MonkeyMan.pm in @INC (@INC contains: /lib /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at ./someutil.pl line 12.
BEGIN failed--compilation aborted at ./someutil.pl line 12.

As I see, the "use lib" line actually works: it adds "/lib" to @INC, but why doesn't it substitute $1?

How would you solve it? Is there any elegant way to find the root directory before loading libraries if the script doesn't know how deep in the directory tree it is?

Lots of thanks to all.

V.Melnik

Replies are listed 'Best First'.
Re: Calculations before using lib;
by choroba (Cardinal) on Aug 04, 2014 at 15:06 UTC
    To call the code during the compilation phase when use is resolved, use the BEGIN block:
    use FindBin qw($Bin); my $dir; BEGIN { if ($Bin =~ /^(\/.+\/monkeyman\/bin)(\/.+)?/) { $dir = "$1/lib"; } use lib $dir; use MonkeyMan;

    See perlsub for details.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      hum... passing undef to lib? Fixed:
      use FindBin qw($RealBin); my @extra_libs; BEGIN { if ($RealBin =~ /^(\/.+\/monkeyman\/bin)(\/.+)?/) { push @extra_libs, "$1/lib"; } } use lib @extra_libs; use MonkeyMan;
      or
      use FindBin qw($RealBin); use lib (); BEGIN { if ($RealBin =~ /^(\/.+\/monkeyman\/bin)(\/.+)?/) { import->lib("$1/lib"); } } use MonkeyMan;

      Note the switch to $RealBin in order to handle symlinks.

      Wow, thank you so much! I thought I've lost my sense %)

      V.Melnik

      As perl needs to know the arguments for use lib before calling lib's import method, every expression used there is implicitly in a BEGIN block equivalent. So I often use a construct like this in my code:

      #!/usr/bin/perl use strict; use warnings; use FindBin (); use lib do { (my $dir=$FindBin::Bin)=~s|/foo/|/bar/|; $dir };

      This can also be used with taint mode to untaint the value of $FindBin::Bin:

      #!/usr/bin/perl -T use strict; use warnings; use FindBin (); use lib do { $FindBin::Bin=~m|^(/.*)| or die "Can't find myself"; "$1/ +../lib" };

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Calculations before using lib; (FindLib)
by tye (Sage) on Aug 04, 2014 at 16:48 UTC

    This sounds exactly like the problem I wrote File::FindLib to deal with. Now, it won't help you much if you just try to put it inside your project directory tree, of course. But I've found it to be a useful egg in that chicken-and-egg problem (I manage to get it added to the standard Perl build that gets deployed and used by our projects).

    - tye        

Re: Calculations before using lib;
by ikegami (Patriarch) on Aug 04, 2014 at 16:11 UTC

    (Nevermind. The script apparently doesn't know where it is relative to its lib dir. I'm not sure how that's possible. Maybe it likes to go on walks alone? Or maybe the lib dir has nothing to do with the script, so PERL5LIB should be used instead.)

    Sounds to me like you could simply use

    use FindBin qw($RealBin); use lib "$RealBin/../lib"; use MonkeyMan;

    or

    use mylib; use MonkeyMan;
Re: Calculations before using lib;
by v_melnik (Scribe) on Aug 06, 2014 at 11:31 UTC
    Lots of thanks to all!
    V.Melnik