Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

use lib relative pathnames

41 views
Skip to first unread message

Dylan Tynan

unread,
Feb 12, 1998, 3:00:00 AM2/12/98
to

I'm having some trouble with modules that use other modules via relative
pathnames & would appreciate some help. Here's the description:

All of my .pm files are in several directories under /home for various
reasons. Some of these perl modules depend on other modules via use
lib. I have three 'environments' under home (dev, tst, prod) each with
it's own copy of the same files. When I build from one environment to
the other I want to just recursively copy the files over. In order for
this to work my "use lib" statements must use relative, not absolute,
pathnames. For example:

/home/dev/modules/stuff
blah.pm does use lib '../misc/junk';
blah2.pm
/home/dev/modules/misc
junk.pm

Some program in some other directory does:
use lib '/home/dev/modules/stuff';
use blah.pm;

It does not use junk.pm since that should be brought in by blah.pm.
However, my 'use lib' statements appear to be relative to my --executing
program-- rather than to the file that contains the 'use lib' statement
(blah.pm in this case). So if my program is in /tmp I end up with:

Can't locate junk.pm in @INC (@INC contains: . ../misc
/usr/local/lib/perl5/aix/5.00401 ...

This seems very weird to me. In searching dejanews this is the behavior
I would expect to get if I had done use FindBin qw ($RealBin); use lib
"$RealBin/../misc"; In fact there seem to be a couple of examples of
people wanting that behavior & those FindBin statements are what's given
as a solution. I -seem- to be getting the FindBin behavior without
using FindBin. By the way, if I put use lib '/home/dev/modules/misc';
into my program in /tmp then it works just fine. Also, if I move the
program into a directory beneath /home/dev/modules it works (thus
proving that it is relative to the executing program as it goes up into
/home/dev/modules then down into misc). Finally, fully qualifying the
use lib statement also solves the problem. Is it brain dead or is there
a good reason?

Am I just misunderstanding this (probably) or is it not working
properly?

Perl version is 5.00401 on AIX 4.1.4.

Now, that's the basic description of the problem. I'll make this post
even longer by trying to head off a few suggestions in advance:

1) Putting my libraries in the standard perl distribution directory
won't fly. It's a shared Unix system, I can't junk up system space with
my own stuff, plus I have 3 versions (dev/tst/prod) of each program so
they can't reside in the same directory, plus I don't have write
permission in those directories (nor can I get it for any length of
time).

2) I haven't tried it but I guess I might be able to work around this
problem using perl -I or using the PERL5LIB (or whatever) environment
variable. This isn't very clean at all though - what I'm doing seems
like something that -should- work. In order to do one of these
workarounds I guess I'd have to basically have a shell wrapper that set
the right variables, then remember to run the right one in each
environment. Not sure how this would affect my shell-magic
#!/usr/bin/perl at the top of each file ... guess it'd have to go away.
The web server that drives most of these scripts would also have to run
some wrapper or be modified to set the right env. variables or something
similar. All yucky solutions.

3) As usual with a weird problem you end up running through all sorts of
bizarre possible solutions. I have tried various things with BEGIN
blocks setting a variable using cwd, $0, FindBin, etc. trying to see if
I could get the current module's directory to prepend to the relative
path, but always end up with the executing program's directory or
current working directory, etc. Anyway, it's very possible that I've
just missed something completely obvious...?

4) For my own sanity (and yours) I reduced my test program & two libs to
the most simple form possible & still recreated the problem. Here they
are:

testprog.pl (located in /tmp)
#!/usr/bin/perl
use lib '/home/dev/modules/stuff';
use blah;
print "testprog\n";

blah.pm (located in /home/dev/modules/stuff)
#!/usr/bin/perl
package blah;
use lib '../misc';
use junk;
print "blah.pm\n";

junk.pm (located in /home/dev/modules/misc)
#!/usr/bin/perl
package junk;
print "junk.pm\n";

Thanks!!!
Dylan Tynan


Randal Schwartz

unread,
Feb 12, 1998, 3:00:00 AM2/12/98
to aix...@ix.netcom.com

>>>>> "Dylan" == Dylan Tynan <aix...@ix.netcom.com> writes:

Dylan> /home/dev/modules/stuff
Dylan> blah.pm does use lib '../misc/junk';
Dylan> blah2.pm
Dylan> /home/dev/modules/misc
Dylan> junk.pm

Dylan> Some program in some other directory does:
Dylan> use lib '/home/dev/modules/stuff';
Dylan> use blah.pm;

Dylan> It does not use junk.pm since that should be brought in by blah.pm.
Dylan> However, my 'use lib' statements appear to be relative to my --executing
Dylan> program-- rather than to the file that contains the 'use lib' statement
Dylan> (blah.pm in this case). So if my program is in /tmp I end up with:

Dylan> Can't locate junk.pm in @INC (@INC contains: . ../misc
Dylan> /usr/local/lib/perl5/aix/5.00401 ...

Dylan> This seems very weird to me.

And it's the way it is. Get used to it. Relative pathnames are
*always* relative to the *current* directory, not to the directory in
which the file is found. You'll need to come up with a different
scheme.

print "Just another Perl hacker," # but not what the media calls "hacker!" :-)
## legal fund: $20,990.69 collected, $186,159.85 spent; just 200 more days
## before I go to *prison* for 90 days; email fu...@stonehenge.com for details

--
Name: Randal L. Schwartz / Stonehenge Consulting Services (503)777-0095
Keywords: Perl training, UNIX[tm] consulting, video production, skiing, flying
Email: <mer...@stonehenge.com> Snail: (Call) PGP-Key: (finger mer...@teleport.com)
Web: <A HREF="http://www.stonehenge.com/merlyn/">My Home Page!</A>
Quote: "I'm telling you, if I could have five lines in my .sig, I would!" -- me

Tom Christiansen

unread,
Feb 12, 1998, 3:00:00 AM2/12/98
to

[courtesy cc of this posting sent to cited author via email]

In comp.lang.perl.modules, Randal Schwartz <mer...@stonehenge.com> writes:
:And it's the way it is. Get used to it. Relative pathnames are


:*always* relative to the *current* directory, not to the directory in
:which the file is found. You'll need to come up with a different
:scheme.

This is something that would come as a surprise to C programmer
accustomed to #include search behaviour.

--tom
--
Tom Christiansen tch...@jhereg.perl.com
Remember though that
THERE IS NO GENERAL RULE FOR CONVERTING A LIST INTO A SCALAR.
--Larry Wall in the perl man page

Graham Barr

unread,
Feb 12, 1998, 3:00:00 AM2/12/98
to

Dylan Tynan wrote:
>
> I'm having some trouble with modules that use other modules via relative
> pathnames & would appreciate some help. Here's the description:

>
> /home/dev/modules/stuff


> blah.pm does use lib '../misc/junk';

Well unfortunately the .. will be relative to the directory which
the the perl process is in at the time @INC is searched, so this
is not very usefull.

> testprog.pl (located in /tmp)
> #!/usr/bin/perl
> use lib '/home/dev/modules/stuff';
> use blah;
> print "testprog\n";

testprog is OK as it uses a full pathname

> blah.pm (located in /home/dev/modules/stuff)
> #!/usr/bin/perl
> package blah;
> use lib '../misc';
> use junk;

perl will search ../misc from the current directory, however as you
know the name of the file you are in you could do

use lib (__FILE__ =~ /^(.*)blah.pm/)[0] . "../misc";

Graham.

--
Originality is the ability to conceal your source.

Zenin

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

Randal Schwartz <mer...@stonehenge.com> wrote:
: And it's the way it is. Get used to it. Relative pathnames are

: *always* relative to the *current* directory, not to the directory in
: which the file is found. You'll need to come up with a different
: scheme.
>snip<

Quite true, but I've found the trick below to work 99.9% of the
time. Yes, it can fail if your arg[0] is munged, but that's not
vary common for most users.

use File::Basename;
use lib dirname ($0) . "../lib"; ## Or whatever
use My::Module;

Hmm, maybe this simple piece of code should be in the FAQ, no?

--
-Zenin
ze...@best.com

Graham Barr

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

Zenin wrote:
> Quite true, but I've found the trick below to work 99.9% of the
> time. Yes, it can fail if your arg[0] is munged, but that's not
> vary common for most users.
>
> use File::Basename;
> use lib dirname ($0) . "../lib"; ## Or whatever

That would insert in @INC a path relative to where the script is,
which is not what the original poster wanted. They were asking
for #includ style searching.

And what you have would not always work as $0 may not contain the
full path the the script, the best way to do that is

use FindBin;
use lib $FindBin::Dir . "/../lib";

or

use lib $FindBin::RealDir . "/../lib";

the difference being that RealBin resolves symbolic links and Bin
does not.

> use My::Module;
>
> Hmm, maybe this simple piece of code should be in the FAQ, no?

If it is that I would suggest the FindBin code as FindBin comes
with perl.

Dylan Tynan

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

Thanks to everyone who responded to my question about 'use lib' with relative
pathnames.

It still seems to me that the "normal" operation of 'use lib' with relative
pathnames from within a use'd file should be to make it relative to that
particular file rather than cwd (the #include-style behavior). To me this
seems more useful, but I guess it's just a mindset thing.

Anyway, I think Graham's solution (listed below) will work fine ... just
requires that I put the .pm filename in itself, but still better than
hardcoding an entire pathname in. At least I can build from one environment
to the next easily.

Thanks again!
Dylan

Graham Barr wrote:

Martin Vorlaender

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

Zenin (ze...@best.com) wrote:

: Randal Schwartz <mer...@stonehenge.com> wrote:
: : And it's the way it is. Get used to it. Relative pathnames are
: : *always* relative to the *current* directory, not to the directory in
: : which the file is found. You'll need to come up with a different
: : scheme.
: >snip<

: Quite true, but I've found the trick below to work 99.9% of the


: time. Yes, it can fail if your arg[0] is munged, but that's not
: vary common for most users.

: use File::Basename;
: use lib dirname ($0) . "../lib"; ## Or whatever

: use My::Module;

: Hmm, maybe this simple piece of code should be in the FAQ, no?

Sorry, but $0 is not guaranteed to contain path information
(it does not on my OS/2 system, for example).

What didn't fail for me yet, is

use FindBin;
use lib "$FindBin::RealBin/../lib"; ## Or whatever
use My::Module;

cu,
Martin
--
| Martin Vorlaender | VMS & WNT programmer
Ceterum censeo | work: m...@pdv-systeme.de
Redmondem delendam esse. | http://www.pdv-systeme.de/users/martinv/
| home: mar...@radiogaga.harz.de

Tom Christiansen

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

[courtesy cc of this posting sent to cited author via email]

In comp.lang.perl.modules, aix...@ix.netcom.com writes:
:It still seems to me that the "normal" operation of 'use lib' with relative


:pathnames from within a use'd file should be to make it relative to that
:particular file rather than cwd (the #include-style behavior). To me this
:seems more useful, but I guess it's just a mindset thing.

No, what you say makes perfect sense. That's how #includes work
with cpp, and is what is expected. I consider it a bug that it
doesn't work that way.

--tom
--
Tom Christiansen tch...@jhereg.perl.com

echo "ICK, NOTHING WORKED!!! You may have to diddle the includes.";;
--Larry Wall in Configure from the perl distribution

Ilya Zakharevich

unread,
Feb 14, 1998, 3:00:00 AM2/14/98
to

[A complimentary Cc of this posting was sent to Martin Vorlaender
<MAR...@RADIOGAGA.HARZ.DE>],
who wrote in article <34e47bfc.5241...@radiogaga.harz.de>:

> Sorry, but $0 is not guaranteed to contain path information
> (it does not on my OS/2 system, for example).

Eh?

I:\emx\src>perl -d -S pod2man

Loading DB routines from perl5db.pl version 1.02
...
DB<1> print $0
F:\EMX.ADD\BIN/pod2man.cmd
DB<2>

I cannot imagine worse scenario than this one. Somehow Perl had a
possibility to find your Perl script - given $0, so why cannot you?

Ilya

Martin Vorlaender

unread,
Feb 14, 1998, 3:00:00 AM2/14/98
to

Ilya Zakharevich (il...@math.ohio-state.edu) wrote:
: Martin Vorlaender <mar...@radiogaga.harz.de> wrote:
: > Sorry, but $0 is not guaranteed to contain path information

: > (it does not on my OS/2 system, for example).

: Eh?

: I:\emx\src>perl -d -S pod2man

: Loading DB routines from perl5db.pl version 1.02
: ...
: DB<1> print $0
: F:\EMX.ADD\BIN/pod2man.cmd
: DB<2>

: I cannot imagine worse scenario than this one. Somehow Perl had a
: possibility to find your Perl script - given $0, so why cannot you?

Hmmm... point taken. But Zenin's method

: use File::Basename;
: use lib dirname ($0) . "../lib"; ## Or whatever

fails in another way on non-Unix OS's (I tried it on OS/2, again. But I
suspect it will behave similarly under Micro$oft's resident program
loaders). Given that most scripts don't reside in PATH directories, I
didn't bother calling them with -S.

[--f:\temp\t\t.pl--]
#!perl
use File::Basename;
print dirname ($0);
[--end--]

F:\TEMP\t> perl t.pl
.\
F:\temp\t> perl .\t.pl
.\
F:\temp\t> perl ./t.pl
.

I don't find this reliable enough to build my scripts onto it. So I repeat:

Ilya Zakharevich

unread,
Feb 14, 1998, 3:00:00 AM2/14/98
to

[A complimentary Cc of this posting was sent to Martin Vorlaender
<MAR...@RADIOGAGA.HARZ.DE>],
who wrote in article <34e5b7ae.5241...@radiogaga.harz.de>:

> [--f:\temp\t\t.pl--]
> #!perl
> use File::Basename;
> print dirname ($0);
> [--end--]
>
> F:\TEMP\t> perl t.pl
> .\
> F:\temp\t> perl .\t.pl
> .\
> F:\temp\t> perl ./t.pl
> .
>
> I don't find this reliable enough to build my scripts onto it.

Why?

Ilya

Martin Vorlaender

unread,
Feb 15, 1998, 3:00:00 AM2/15/98
to

Ilya Zakharevich (il...@math.ohio-state.edu) wrote:
: Martin Vorlaender <mar...@radiogaga.harz.de> wrote:
: > [--f:\temp\t\t.pl--]

: > #!perl
: > use File::Basename;
: > print dirname ($0);
: > [--end--]
: >
: > F:\TEMP\t> perl t.pl
: > .\
: > F:\temp\t> perl .\t.pl
: > .\
: > F:\temp\t> perl ./t.pl
: > .
: >
: > I don't find this reliable enough to build my scripts onto it.

: Why?

Because depending on how the script was called (using / or \ as the last
path delimiter), the script must or must not append a path delimiter to
be able to further qualify the path. I consider this a bug. Looking into
the source, I guess it's only being exercised in OS/2.

As I found another issue with File::Basename and OS/2, I'll file a bug
report to the author.

Zenin

unread,
Feb 15, 1998, 3:00:00 AM2/15/98
to

Martin Vorlaender <mar...@RADIOGAGA.HARZ.DE> wrote:
: Hmmm... point taken. But Zenin's method

: : use File::Basename;
: : use lib dirname ($0) . "../lib"; ## Or whatever
>snip<
: What didn't fail for me yet, is

: use FindBin;
: use lib "$FindBin::RealBin/../lib"; ## Or whatever
: use My::Module;
>snip<

Ya know, I first read of the FindBin module some time ago (the Blue
Camel?) and a mention that while it was not included in the standard
distribution (and it wasn't, I checked), it could be found at CPAN.
I searched every file on CPAN at the time (I have a current full
local copy at work) and found nothing.

It's at this point I thought about the issue and came up with the
dirname $0 idea. Since it seemed just as easy (2 lines of code, no
worse then FindBin), and since it's worked perfectly for me so far
(it's vary rare I'm on a non-Unix machine or have my arg[0] munged by
other forces) it's what I've stuck with and didn't think twice about
it unless I had to. -There have been cases (such as funky setuid
kernel handlers) that my arg[0] get's munged and I had to use other
ideas ((blagh) hard coding lib paths, etc). I figured since it does
work so well on real OSes, FindBin must just have been given up on
since I could not find any mention of it again until now.

Martin and Graham are correct, it's a better and more reliable way
to do the same thing. Use FindBin, not my other method. -It's close
to the same thing (check the FindBin.pm code) but with a *huge*
number of much needed system checks, work arounds, and other black
magic for consistency and reliability on broken OSes.

Graham or anyone, what version of perl did the FindBin module became
part of the standard lib? It would be *vary* nice if every standard
lib module included a note in the pod with the version of perl it
first appeared in, much the same as good man system man pages.

--
-Zenin
ze...@best.com

Ilya Zakharevich

unread,
Feb 15, 1998, 3:00:00 AM2/15/98
to

[A complimentary Cc of this posting was sent to Martin Vorlaender
<MAR...@RADIOGAGA.HARZ.DE>],
who wrote in article <34e69c5b.5241...@radiogaga.harz.de>:

> Ilya Zakharevich (il...@math.ohio-state.edu) wrote:
> : Martin Vorlaender <mar...@radiogaga.harz.de> wrote:
> : > [--f:\temp\t\t.pl--]
> : > #!perl
> : > use File::Basename;
> : > print dirname ($0);
> : > [--end--]
> : >
> : > F:\TEMP\t> perl t.pl
> : > .\
> : > F:\temp\t> perl .\t.pl
> : > .\
> : > F:\temp\t> perl ./t.pl
> : > .
> : >
> : > I don't find this reliable enough to build my scripts onto it.
>
> : Why?
>
> Because depending on how the script was called (using / or \ as the last
> path delimiter), the script must or must not append a path delimiter to
> be able to further qualify the path.

Why?

>perl -e "print 1 if -f 'socket.ph'"
1
>perl -e "print 1 if -f './socket.ph'"
1
>perl -e "print 1 if -f '.\/socket.ph'"
1

Ilya

P.S. Not that it cannot be made prettier, but it is definitely not
breaking anything.

Martin Vorlaender

unread,
Feb 16, 1998, 3:00:00 AM2/16/98
to

[posted and emailed]

Ilya Zakharevich (il...@math.ohio-state.edu) wrote:
: >perl -e "print 1 if -f 'socket.ph'"


: 1
: >perl -e "print 1 if -f './socket.ph'"
: 1
: >perl -e "print 1 if -f '.\/socket.ph'"
: 1

Hmmm...

>perl -e "print 1 if -f './//socket.ph'"
1

That's it. I give up. Let's end this, okay?

Thou knowest of depths in this realm that I did not dream of trying.

(I should have known from the start that arguing with the same Ilya
that is mentioned in the AUTHORS section of my Readme.OS2 gets me
nowhere... :-)

Graham Barr

unread,
Feb 16, 1998, 3:00:00 AM2/16/98
to

Zenin wrote:
> Graham or anyone, what version of perl did the FindBin module became
> part of the standard lib? It would be *vary* nice if every standard
> lib module included a note in the pod with the version of perl it
> first appeared in, much the same as good man system man pages.

I am not sure when it was first included, but it is definitely
in the perl5.004_04 distribution.

It is also available on CPAN, and always has been so I do not
know why you could not find it.

It's CPAN location is

http://www.perl.com/CPAN-local/authors/id/GBARR/FindBin-1.04.tar.gz

Ilya Zakharevich

unread,
Feb 16, 1998, 3:00:00 AM2/16/98
to

[A complimentary Cc of this posting was sent to Martin Vorlaender
<MAR...@RADIOGAGA.HARZ.DE>],
who wrote in article <34e7a47f.5241...@radiogaga.harz.de>:

> (I should have known from the start that arguing with the same Ilya
> that is mentioned in the AUTHORS section of my Readme.OS2 gets me
> nowhere... :-)

File the bug report with dirname anyway. The fact that it does not
break the things yet does not mean that it should not be fixed.

Ilya

Justin Vallon

unread,
Feb 17, 1998, 3:00:00 AM2/17/98
to

Dylan Tynan <aix...@ix.netcom.com> writes:

> Anyway, I think Graham's solution (listed below) will work fine ... just
> requires that I put the .pm filename in itself, but still better than
> hardcoding an entire pathname in. At least I can build from one environment
> to the next easily.
>
> Thanks again!
> Dylan
>
> Graham Barr wrote:
>
> > perl will search ../misc from the current directory, however as you
> > know the name of the file you are in you could do
> >
> > use lib (__FILE__ =~ /^(.*)blah.pm/)[0] . "../misc";

Of course, a creative regexp would eliminate the need to know the filename.

# untested
use lib (__FILE__ =~ /^(.*/|)/)[0] . "../misc";
# Find longest prefix ending in /, or else empty if no slash.

Or, just use File::Basename.

# tested, and it works!
use File::Basename;
use lib File::Basename::dirname(__FILE__) . "/../misc";
use other;

Or, use a BEGIN block.

# also works, but less surprising.
use File::Basename;
BEGIN { push @INC, File::Basename::dirname(__FILE__) . "/../misc"; }
use other;

> >
> > Graham.
> >
> > --
> > Originality is the ability to conceal your source.

--
-Justin
val...@bear.com

Graham Barr

unread,
Feb 17, 1998, 3:00:00 AM2/17/98
to

Justin Vallon wrote:
> > > use lib (__FILE__ =~ /^(.*)blah.pm/)[0] . "../misc";
>
> Of course, a creative regexp would eliminate the need to know the filename.
>
> # untested
> use lib (__FILE__ =~ /^(.*/|)/)[0] . "../misc";
> # Find longest prefix ending in /, or else empty if no slash.

That is not portable to multiple platforms. But that may be OK for
the user

> Or, just use File::Basename.
>
> # tested, and it works!
> use File::Basename;
> use lib File::Basename::dirname(__FILE__) . "/../misc";
> use other;

No that I like better than mine. All we need now is a module to
join names togther into a path. So we could do something like

use File::Basename qw(dirname);
use lib make_path(dirname(dirname(__FILE__)),"misc");
use other;

It looks ugly, but would be more portable. Hm maybe dirname could accept
a depth argument so you would only need

use lib make_path(dirname(__FILE__,1),"misc");

> Or, use a BEGIN block.
>
> # also works, but less surprising.
> use File::Basename;
> BEGIN { push @INC, File::Basename::dirname(__FILE__) . "/../misc"; }
> use other;

lib.pm was written so the user did not have to write a BEGIN block.
Also this may or may not work. If other.pm uses DynaLoader to load
a compiled extension then this approach will not work, use lib will
solve that for you.

Of course again it depends on what the user want.

William R. Ward

unread,
Feb 18, 1998, 3:00:00 AM2/18/98
to

How about this idea: a new module, rlib.pm, which is just like lib.pm
except that it uses relative (hence the the "r" in "rlib") paths. We
already have lib and blib, why not rlib?

--Bill.

--
William R Ward Bay View Consulting http://www.bayview.com/~hermit/
her...@bayview.com 1803 Mission St. #339 voicemail +1 408/479-4072
her...@cats.ucsc.edu Santa Cruz CA 95060 USA pager +1 408/458-8862
PGP Key 0x2BD331E5; Public key at http://www.bayview.com/~hermit/pubkey.txt

Graham Barr

unread,
Feb 18, 1998, 3:00:00 AM2/18/98
to William R. Ward

In comp.lang.perl.misc William R. Ward wrote:
>
> How about this idea: a new module, rlib.pm, which is just like lib.pm
> except that it uses relative (hence the the "r" in "rlib") paths. We
> already have lib and blib, why not rlib?

How about this.

package rlib;

use lib ();
use File::Basename qw(dirname);
use Cwd qw(abs_path);

sub import {
shift;
my $dir = abs_path(dirname((caller)[1]));
lib->import( map { $dir . "/" . $_ } @_);
}

sub unimport {
shift;
my $dir = abs_path(dirname((caller)[1]));
lib->unimport( map { $dir . "/" . $_ } @_);
}

1;

__END__

=head1 NAME

rlib - manipulate @INC at compile time with relative paths

=head1 SYNOPSIS

use rlib LIST;

no rlib LIST;

=head1 DESCRIPTION

rlib works in the same way as lib, except that all paths in LIST or
first prefixed with the directory path of the current file before
being added to or removed from @INC.

=head1 SEE ALSO

lib - module which adds paths to @INC

=head1 AUTHOR

Graham Barr <gb...@pobox.com>

=cut

0 new messages