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

I'm working with some legacy code that I'd like to tweak, so it's easier to develop and test, and so that its users can use older versions of the code if the current production version happens to have a serious bug.

Give or take, it's in /x/foo/bin and /x/foo/lib, where "foo" is owned by my team and is only used for this particular code base.

My plan is to deploy new versions as, e.g., /x/foo/v1.2/bin and /x/foo/v1.2/lib, and then make the real "bin" and "lib" into symlinks that point to what's in /x/foo/v1.2.

Question is, how best to configure the code so that you can:

as unobtrusively as possible.

If a script has:

use lib "/x/foo/lib";

in it, it's hard-wired to use the production version of the code. If I do something like this instead:

use vars qw( $ROOT );
BEGIN { $ROOT = (defined $ENV{XX_ROOT}) ? $ENV{XX_ROOT} : "/x/foo" }
use lib "$ROOT/lib";

then I get the production version by default, or I can set an XX_ROOT environment variable to point to somewhere else (development code, old production code, whatever) if I want to use a different version, and things work.

However, that's three lines of boilerplate that I have to put into every script (and potentially every library), and seems ugly.

Is there a clean, standard way to do this sort of thing?

Replies are listed 'Best First'.
Re: layout/configuration of deployed files
by techcode (Hermit) on Mar 02, 2011 at 18:30 UTC
    How about relative path of lib: "use lib '../lib/';

    Also, as suggested any developer can change his own PERL5LIB env variable - and put the /my/dev/his-spot/lib as first thing. If you have someone naming modules as existing and widely used Perl ones ... you're screwed sooner or latter :)

    You can always just make a new namespace and put new stuff there. As in CompanyName::* - so CompanyName::System (CompanyName/System.pm)


    Have you tried freelancing/outsourcing? Check out Scriptlance - I work there since 2003. For more info about Scriptlance and freelancing in general check out my home node.
      A new namespace sounds like a great idea. Thanks!
Re: layout/configuration of deployed files
by Eliya (Vicar) on Mar 02, 2011 at 16:58 UTC
    However, that's three lines of boilerplate

    Have you considered using PERL5LIB instead of your XX_ROOT-controlled use lib?

    It essentially serves the same purpose as use lib, but can be controlled dynamically from the environment, which is what you seem to want to do.

      Indeed... Sorry for not mentioning that.

      One of my goals is to keep this code as isolated from everything else as I possibly can. To that end, I'd like to avoid the situation where, for example, one of our modules is given an unfortunate name (like "Symbol"), and gets used (and causes problems) because we're in somebody's PERL5LIB.

      I realize that we're in the realm of process problems, here, because developers should know better than to name something that way... On the other hand, anything I can do to make this as bulletproof as possible will only serve to help me in the long run, I think. :-)

        I'd like to avoid the situation where, for example, one of our modules is given an unfortunate name (like "Symbol"), and gets used (and causes problems) because we're in somebody's PERL5LIB.

        So if I'm understanding you correctly, you want to avoid the following problem (sample case):

        #!/usr/bin/perl use IO::Handle; print "OK\n";

        Now, let's say you have a private module "Symbol.pm", which also happens to be used by IO::Handle.  So,

        $ touch Symbol.pm $ ./891005.pl OK $ PERL5LIB=. ./891005.pl Symbol.pm did not return a true value at /usr/local/perl/5.12.2/lib/5. +12.2/x86_64-linux-thread-multi/IO/Handle.pm line 264. BEGIN failed--compilation aborted at /usr/local/perl/5.12.2/lib/5.12.2 +/x86_64-linux-thread-multi/IO/Handle.pm line 264. Compilation failed in require at ./891005.pl line 3. BEGIN failed--compilation aborted at ./891005.pl line 3.

        (In the first case, IO::Handle loads the correct system version of Symbol.pm, while in the latter case, IO::Handle loads your private variant, because it's found first along the search paths.)

        I'm not sure, however, how your approach would help here.  In other words, you'd get the exact same problem in case you had use lib "."; at the top of the script.

        Of course, you could fiddle with the order of the use statements, but that seems very fragile, too (in more complex, real-world scenarios).  And if you want to go that route, I don't think there's really any way around having to carefully place boilerplate code in all of your modules...  IMHO, choosing an unambiguous namespace for your own code is the better option.

        Or is it some other problem you're worried about?

Re: layout/configuration of deployed files
by Tuppence (Pilgrim) on Mar 02, 2011 at 20:15 UTC

    keep in mind that you could easily hide your interface to getting the right library behind a package

    something like use theRightLibrary;

    and then have theRightLibrary.pm do the actual use vars / use lib dance.

    Does make you add theRightLibrary.pm to your old code, but once all code instances have the module available every script you have will work the same way.

Re: layout/configuration of deployed files
by Eliya (Vicar) on Mar 02, 2011 at 22:14 UTC

    You could also reduce your boilerplate to one line:

    use lib $ENV{XX_ROOT} ? "$ENV{XX_ROOT}/lib" : "/x/foo/lib";
      Use the FindBin module.
      use FindBin; use lib "$FindBin::Bin/../lib";