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

Hello Monks,

I seek your wisdom one more time to package scripts in such a way that there is no conflict with existing Perl applications.

Here is the scenario. I developed some scripts using Activestate Perl 5.8.9.825 for Windows and Linux platform. My scripts are dependent on modules like Date::Formatter, Config::IniFiles etc.

I manually installed these modules on my first target machine (Windows 2003 server). I did not realize that Oracle was also installed on the same machine, which also uses some older versions of Perl scripts. My scripts did not work right out of the box. I had to set the environment variable PERL5LIB to c:\perl\lib in every command session where I ran my scripts. I did not want to make this change permanent, I could broken existing Oracle installation.

So here are my questions:

1. Can I somehow package all the dependent modules along with my scripts so that they get installed in my application folder, rather than under perl/lib?
2. Would you call it a good practice?
3. Do I even need to tinker with PERL5LIB variable?

Please do suggest anything which you consider as a good practice for deploymnt. I am sure my way was kludgy.

Thank you so much in advance.

Ash

  • Comment on Question: Best practices for packaging scripts

Replies are listed 'Best First'.
Re: Question: Best practices for packaging scripts
by Bloodnok (Vicar) on Mar 26, 2009 at 15:52 UTC
    I don't know about package the dependent modules, but if you're installing each dependent module manually (having first downloaded it) you can use the PREFIX macro to specify an alternative installation directory when generating the make script e.g. perl Makefile.PL PERFIX=path/to/some/other/dir, and then subsequently i.e. run-time, use PERL5LIB to point to that same alternative directory.

    It would appear that the above installation behaviour can be emulated when using CPAN driven installation by utilising the makepl_arg CPAN config option - but never having used this option, I can't vouch for it...

    A user level that continues to overstate my experience :-))
Re: Question: Best practices for packaging scripts
by ELISHEVA (Prior) on Mar 27, 2009 at 13:45 UTC

    I would try to isolate your application as much as possible from the system it is being installed in. I view applications as guests on a machine - they should leave everything the way they found it.

    This is clearly easier to do if everything used by your application is self-contained. When items are shared, uninstalling an application can be really difficult. Unless your application is being installed by a package manager that keeps track of how many applications are using module X (e.g. you wrapped your CPAN modules in a .deb or .rpm), you won't be able to tell if a shared module X might be in use by some other application.

    Keeping every thing separate also saves you from a nightmare of version compatibility issues. You don't have to worry about your application breaking if other applications need older or newer versions of a module.

    Yes, this means you might have more than one copy of module X, but at least you won't be in Perl's version of DDL/JAR hell. A common store of non-core modules for all applications is generally only a good idea if you are running a server box or test machine dedicated to a set of applications or administration scripts where you have full control over what versions will need.

    Manually setting up PERL5LIB is a pain. When I produce self-contained applications, I generally put the modules in a sub-directory. I always know its location relative to the script and hard code that relative path. When the script starts up, I get the location of the script using some script magic (see below), and then convert the hard coded relative path into a real path and add the real path to @INC

    Getting the actual location of the script is a bit tricky. Two things can get in the way: symbolic links and mod_perl. In *nix systems there is a long standing practice of placing a symbolic link to scripts in either /usr/bin or /usr/local/bin. Thus __FILE__ contains a symbolic link rather than the actual location of the file. To get the scripts actual location from the script alone, one has to get to the real file behind the symbolic link. To simplify the process of finding the real script, you can use the module Find::Bin

    I haven't done much work with mod_perl (we use FastCGI), but as I understand it mod_perl presents another problem: scripts are loaded as modules. This means that __FILE__ is often only a partial path. To find the module's actual location, one needs to look up the module in %INC.

    The script below illustrates a way to find the location of your module directory (e.g. myapp/lib) from your script file's location:

    # placed in a begin block so it gets executed before we # try to load any modules. use XXX statements for non-core # modules should be placed after this begin block BEGIN { # Taint mode will complain unless you set these paths. $ENV{PATH} = ""; $ENV{CDPATH} = ""; sub calcLib { if (caller(3)) { #this is loaded as module: caller(3) returns the files caller #can't use __FILE__ between {} my $sModuleFile = __FILE__; $sModuleFile = $INC{ $sModuleFile }; return $sModuleFile =~ /bin\/[^\/]+$/ ? "$sModuleFile/../../lib" : "$sModuleFile/.."; } else { #this is the main entry point: caller(3) returns no caller #use FindBin to get the full file path of this directory #untaint FindBin::Bin my $sModuleDir = $1 if ($FindBin::Bin =~ m/^(.*)$/); return $sModuleDir =~ m/\/bin$/ ? $sModuleDir . "/../lib" : $sModuleDir; } } use lib (calcLib); }
      Awesome! Thanks, this is the kind of input I was looking. I agree with you 100% as the applications being a guest on a machine. I do not care about duplication of modules. Disk space these days is cheaper than oil!!!

      You mentioned, "Getting the actual location of the script is a bit tricky".

      Can't I use "./lib" as the location of modules? It works both on Windows and Unix.

      Ash

        Use "./lib" only works if the following two statements are true

        • the current working directory is set to appdir
        • the modules are stored in appdir/lib

        The only way to make sure that the current working directory is set to the application directory is to write a script that changes to "known" directory location. But how are you to know that directory location? You could force your user to store that application in a hard coded location, but that really isn't very guest-like.

        You could let your user choose a location and then set an environment variable during registration, but then you have no way of making sure that the application location and the environment variable stay in sync. You could educate your user about the environment variable, but now you have yet one more thing to teach and your users have yet one more thing that could go wrong.

        But even if your users were happy with the environment variable, would they be happy with being forced to change directories to the application directory every time the script starts up? And maybe the system is set up so that users can read and execute script files but they are not allowed to go exploring in application directories (e.g. they don't have permission to make the application directory their current working directory)?

        So in the main it is just more reliable to locate your application script without relying on cd or the current working directory. The script above avoids this by using some tricks to deduce the fully qualified path to the application. Then it appends a relative path.

        The actual relative path will depend on the layout of the application directory. In the script above, the layout is

        • appdir/bin/myscript.pl - location of script
        • appdir/lib - location of modules

        The relative path is "../lib". Thus in "$fullPathOfScript/../lib", ".." moves up from the script's own directory to its parent directory, i.e. from appdir/bin to appdir. "lib" moves back down again to appdir/lib

        Hope that helps, beth

Re: Question: Best practices for packaging scripts
by knbknb (Acolyte) on Mar 26, 2009 at 18:09 UTC
    Doesn't activestate perl contain some packaging tool, kind of a GUI-wizard to turn a script into an executable? Never used it, though, and this would work for windows only, not on linux. Just saying.
Re: Question: Best practices for packaging scripts
by Perlbotics (Archbishop) on Mar 26, 2009 at 20:04 UTC

    Maybe PAR might be worth a try?