Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Setting library path from ENV 1

Status
Not open for further replies.

ColinFine

IS-IT--Management
Apr 4, 2005
4
0
0
GB
The immediate question:
Is there a way I can set the library path depending on %ENV (specifically some HTTP/HTML $ENV's) in time to control the loading of Perl modules?

Background:
I have a CGI script, and a whole slew of locally written Perl modules. The script contains a suitable
use lib '/home/sources/bin/perllib';
before it attempts to load any of the local modules.

I have a development server, with several development trees, each of which contains a copy of the script, and some of them contain copies of the modules.
Up to now, if I want the script on the development server to use development versions of the libraries, I edit the text to contain
use lib '/export/home/etgdev-2/perllib';
instead.
(Note that the location of the modules on the live server is not in the same tree, so I can't use a relative path or FindBin).

What I would like to do, is to make this decision automatic, so that I wouldn't have to edit the file. I first tried

Code:
if (virtual_host() =~ [i]a suitable regexp[/i]) {
  use lib "/export/home/$1/perllib";
} else {
  use lib "/home/sources/bin/perllib";
}

That doesn't work, because use lib runs at compile time.
So my next attempt was

Code:
BEGIN {
  my $vh = $ENV{HTTP_X_FORWARDED_HOST} ||
           $ENV{HTTP_HOST} ||
           $ENV{SERVER_NAME};
  if ($vh =~ [i]the same regexp[/i]) {
    use lib "/export/home/$1/perllib";
  } else {
    use lib "/home/sources/bin/perllib";
  }
}
(I took the list of variables to check from CGI.pm)

This still doesn't work, in two respects:
First it still loads the main modules, and secondly, it gives a Perl warning in the log file about an unitialised value in concatenation on the line with the $1 above.

If I put a print STDERR in before the use lib line, it writes what I would expect to the log file - but it appears after the warning.

It seems to me, therefore, that even though I've put the code in a BEGIN block, there are still two stages:
- first, it's compiling the use lib line, and complaining that $1 is unset - presumably because it hasn't yet set up the %ENV hash.
- then it's running the statements in the BEGIN block, and by this time it has set up %ENV, so the output is as expected.

Does anybody know if
1) My analysis is correct?
2) This is a bug or a feature?
3) If there is a way round (or a better way to achieve what I'm trying to do)?

I'm using Gnu perl 5.6.1, Apache 1.3.22 on Fedora Core 3.
 
To answer your points:

1. More or less. Use is always resolved before any code is executed in the current script, BEGIN blocks not excepted. The $1 will always be undefined when this occurs since the regexp will never have been evaluated at this stage.

2. It's a feature. It's doing exactly what it should do. Require is the equivalent for run-time evaluation, but that doesn't support the lib notation.

3. Two simple solutions come to mind. Either have a seperate startup file for each (virtual) server (something I always have, so this would be the simplest for me), which is invoked by the httpd.conf file with:
Code:
PerlRequire "<path>/startup.pl"
In each startup.pl file have the unique paths you wish to set, e.g:
Code:
use lib qw(/export/home/etgdev-2/perllib);

The other alternative is to have a single startup file for all your (virtual) servers, which therefore needs to be able to distinguish between them when adding the library path, i.e. back to your original problem. As this cannot be done with the use lib construct, it must push directly to the global @INC instead.

In httpd.conf, use:
Code:
PerlRequire "<path>/setperllib.pl"

Then recap your original code choice in setperllib.pl:
Code:
use vars qw(@INC);
if (virtual_host() =~ a suitable regexp) {
  push @INC, "/export/home/$1/perllib";
} else {
  push @INC, "/home/sources/bin/perllib";
}

You could also use this second method to solve the code you are currently trying, but this seems cumbersome given that modperl in Apache is persistent, so you really only need to add the library path once, at startup.

Note that the second method doesn't check for repeated insertions into @INC - not doing so doesn't create errors but is wasteful if there is a chance of repeat execution. This possibility doesn't apply to a modperl startup file, but will apply if you use this code within modperl executables, at which point you may want to use something like
Code:
if (virtual_host() =~ a suitable regexp) {
  $_ = "/export/home/$1/perllib";
} else {
  $_ = "/home/sources/bin/perllib";
}
unless (grep(/$_/, @INC)) {
  push @INC, $_;
}
to check @INC before pushing.

...Orac.
 
Thanks, that's very helpful for confirming what's going on.

Your suggestions all seem to assume that I'm using mod_perl, which I'm not.

Colin

Colin Fine
Engineering Tools Group
Pace Micro Technology plc
 
The last method will work for any Perl flavour. By using @INC directly rather than use lib, you can modify the path for modules retrieved by require.
 
A colleague has just given me the answer (used in other Perl scripts here):

The code making the choice needs to be in the BEGIN block, but the 'use lib' needs not to be.

I didn't realise that a BEGIN block is run before compiling the rest of the file.



Colin Fine
Engineering Tools Group
Pace Micro Technology plc
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top