How do I specify the Perl include path for a Catalyst fastcgi process? - perl

i have installed all my dependencies for Catalyst in ~/perl5/lib/perl5 using local::lib
I want to run my app under Apache2 using mod_fcgid, but the fastcgi perl script cannot find the modules in my custom path. How can I specify that (apache config?) my custom lib dir is to be included in the INC directory without explicitly hacking it into myapp_fastcgi.pl? I want to be able to move my app between servers that have the perl includes installed in different directories.
Thanks,
Rob

You can set the environment variables with DefaultInitEnv. You probably want something like this, but with your regular includes too,
DefaultInitEnv PERL5LIB /home/rob/perl5/lib/perl5
You can also do things like setting your app config like that too,
DefaultInitEnv APPNAME_CONFIG /srv/app/appname.conf

Related

How to make a Dist::Zilla based Perl module (or app) install files into /etc/?

I maintain multiple Perl written (unix-ish) applications whose current installation process consists of a manually written Makefile and installs configuration files into /etc/.
I'd really like to switch their development to use Dist::Zilla, but so far I haven't found any Dist::Zilla plugin or feature which allows me to put given files into /etc/ when the make install (or ./Build install in case of using Module::Build instead of ExtUtils::MakeMaker) is run by the local administrator who's installing my application.
With pure ExtUtils::MakeMaker, I could define additional make targets in MY::postamble and the let the install target depend on one of them via the depend { install => … } attribute. Doing something similar, but via dzil build, would probably suffice, but I'd appreciate a more obvious way.
One orthogonal approach would be to make the application not to require the files under /etc/ to exist, but for just switching to Dist::Zilla that seems to much change in the actual code despite I only want to change the build system for now.
For the curious: the two applications I currently have in mind for switching to Dist::Zilla are xen-tools and unburden-home-dir.
The best thing to do is to avoid installing files into /etc from any Perl distribution. You cannot ensure that the cpan client (or the installing user) has permissions to install there, and there can be multiple Perls installed on a system, so each one of them would clobber the /etc files of another install. You can't really prevent the file from being overwritten by a subsequent install, so you shouldn't put config data there that you don't want to lose.
You could put the config file in /etc/, if the application knows to look for it there, but you should allow for that path to be customized (say on a test system, look for the file in the local directory, or in a user's home directory).
For installing read-only module-specific data, the best practice in Perl is to install into a Perl-install-specific location, and the module to do that is File::ShareDir::Install. You can use it from Dist::Zilla using the [ShareDir] plugin, Dist::Zilla::Plugin::ShareDir. It is even included in the [#Basic] plugin bundle, so if you use [#Basic] in your dist.ini, you don't need to do anything at all, other than drop your data files into the share/ directory in your distribution repository.
To access the contents of the sharedir from code, use File::ShareDir.
For porting a complex module installer to Dist::Zilla, I recommend my plugins MakeMaker::Custom or ModuleBuild::Custom, depending on which installer you prefer. These allow you to keep your existing Makefile.PL or Build.PL and just have Dist::Zilla plug in necessary bits like the dependencies.

Why is prefix=/path/to/perllib not enough to set PERL5LIB?

If I create a Perl module and install it as
perl Makefile.PL prefix=/path/to/perllib
Then I would expect to be able to set PERL5LIB to /path/to/perllib and the module be found. This doesn't seem to be the case. The module gets installed to /path/to/perllib/site_perl/5.8.5/, so that's what PERL5LIB has to be set to (or passed to "use lib").
Am I doing something wrong in how I install the modules or how I set PERL5LIB?
You're conflating PREFIX= and LIB=.
A distribution consists of more than just modules. Documentation, for one, isn't installed in the same directory as the modules themselves. PREFIX= tells Perl the base path for all components.
LIB= tells Perl where to install modules.
Use (for example)
perl Makefile.PL PREFIX=~ LIB=~/lib/perl5
Then you'd use
export PERL5LIB=~/lib/perl5
No, that's how it works. The prefix indicates the root of the installation, but the library directories are usually somewhere deeper within that directory structure.
If you install multiple modules with the same prefix, they should all be able to use the same PERL5LIB value, though.
Long story short, use INSTALL_BASE instead of PREFIX. It works more like the --prefix of other installers and creates a predictable install pattern. Then you can set PERL5LIB to <INSTALL_BASE>/lib/perl5 and go.
ExtUtils::MakeMaker::FAQ explains:
The behavior of PREFIX is complicated and depends closely on how your Perl is
configured. The resulting installation locations will vary from machine to machine
and even different installations of Perl on the same machine. Because of this, its
difficult to document where prefix will place your modules.
In contrast, INSTALL_BASE has predictable, easy to explain installation locations.
Now that Module::Build and MakeMaker both have INSTALL_BASE there is little reason
to use PREFIX other than to preserve your existing installation locations. If you
are starting a fresh Perl installation we encourage you to use INSTALL_BASE. If you
have an existing installation installed via PREFIX, consider moving it to an
installation structure matching INSTALL_BASE and using that instead.
What version of perl? You should only have to specify the path up to 'site_perl'. For libraries not under site_perl, you should also specify the path up to 'perllib'. More recent versions of perl (5.8 or better?) are better at letting you specify only these two directories, without having to specify version and architecture directories.

How do I tell CPAN.pm to use an application-specific mini-cpan?

Is there a way to use app-specific mini-CPANs (to ensure specific module versions for different apps and make an install work without net access). The 'standard' mini-cpan way of things relies on setting 'o conf urllist' with a file url in your CPAN/Config.pm
That's fine with one single mini-cpan. However, I need to automate having a CPAN install from a different directory for each app (local to the app), as each app has different version requirements.
The following almost works, but then has only a partially working shell, and still fetches from the web instead of using the mini-cpan from $file_url location:
use CPAN;
use FindBin qw($Bin);
my $file_url="file:///$Bin/../cpan";
CPAN::Shell->o(qw( conf urllist ),$file_url);
CPAN::shell();
You load a different configuration file for each application. The latest cpan command has a -j switch to do just that:
$ cpan -j some/app/Config.pm -i Foo::Bar
This latest feature isn't included in the CPAN.pm distribution yet since it's experimental. I've been using it for several months, however, as part of my DPAN work.
If you don't like any of that, you just have to provide your application-specific CPAN::Config module somewhere Perl will find it before it finds your personal or site-wide version. Copy your current CPAN/Config.pm into your application modules directory and ensure that the directory is first in #INC (perhaps using the PERL5LIB environment variable). CPAN.pm should find that one first and use it. It only uses the first one it finds. This is handy when the configuration is fixed, although it's a bit flexible since you can run code in the module. It's configuration as Perl code.
If that is not good enough for you, you can override CPAN::HandleConfig() in your application so CPAN.pm doesn't load any files. You then set the values yourself. This is especially handy when you have a lot of application logic to resolve to figure out some of the configurations values. The CPAN.pm configuration is just the %CPAN::Config hash, so you just have to set the right values for the appropriate keys.
In any case, you probably want to set the "connect_to_internet_ok" configuration to a false value and clean out your "urllist" to have only the local minicpans.

How can I set Perl's inclusion path for modules for a CGI script?

I’ve got several Perl modules installed on my hosting machine in ~/perl, how do I add them to Perl module path? Setting PERL5LIB or unshifting the paths to #INC surely works, but the environment variable does not help when running as a CGI script and the #INC way is not very portable. Is there a better way? This has to be a common problem, am I missing something?
A cleaner way to do it imo is :
use lib "/path/" ;
there are other interesting and good ways to do it that can be found here:
http://www.slideshare.net/pfig/cpan-training-presentation/
Also, re: CGI scripts, you CAN define PERL5LIB paths for your CGI Env, it just depends on your webhost.
Some Webhosts permit specific control of env variables for the CGI Enviroment, and you have to just set them, others may have a "permit" list that allows variables visible at start time appear in local scope.
May help to read up on how to do this on your specific HTTP server.
There is one more option, if setting ENV is not to your taste: local::lib
Which searches some predefined paths automatically.
PERL5LIB works just fine for CGI scripts. You just have to set the variable in the right place, such as the server configuration. Which webserver are you using? For Apache, I use the SetEnv directive from mod_env.
#INC is portable. The paths you put in there may not be the same on every machine, but you shouldn't have a problem the with variable itself.
Have you read the FAQ entries in perlfaq8:
How do I add the directory my program lives in to the module/library search path?
How do I add a directory to my include path (#INC) at runtime?

How can I create a portable perl when I can't install modules on the target host?

I need to run Perl applications I develop on cygwin Windows on HP unix / Solaris hosts. I am not a superuser on the unix machines and I can't touch the default Perl module location nor can I install modules to the default Perl module location. Also the unix installation lacks most basic modules and I can't change that.
For example, I have a Perl application that needs Expect which has native C compiled parts to it. How would I roll out this application to unix with its required dependencies without having to install anything else on that box?
Is there way to build the entire Perl application under Cygwin Windows and then just roll out one executable to unix and run it from my home directory there?
EDIT addition based on answers so far:
Thanks in particular to brian, the local LIB dir solution seems to work in case of native Perl, but in case of Perl module needing C components, cross platform compiling, ie compiling on cygwin to run on Solaris, is not really possible as I feared.
However would having an other linux installation help, i.e. would this be possible easier between different flavors of Unix like package Perl on linux and then deploy to Solaris/HP? And what about something like lcc ?
Also I'd still like to hear little more if somebody has rolled out a native Perl package on Windows that includes all dependencies for a complicated Perl app that can then be moved to unix as just one file? (I do now understand that it won't work in case native C code is included like in in Expect.pm, but what about in case of app only using pure perl modules?)
Basically for many reasons I am trying to minimize time I need to spend being logged into these "production" unix hosts and do as much as possible locally beforehand.
Added a new cross-compile question, since I felt I was maybe veering too far from the original perl question.
EDIT -- Par looks promising for pure Perl, although same deal, it doesn't look to solve the cross platform compile problem for native extensions
In this case, I'd consider delivering a complete application complete with its own Perl. You get to choose any version you like and any modules you like. Compile everything, organize everything into a directory, then tar the result. To deploy, copy the file and untar. Use the advice that others have already noted about library search paths, etc. In essence, your application gets its own stack.
Now, the trick there is the cross compilation. Why are you developing on Cygwin? Is that a target too? Is there a reason you don't have an HP/UX or Solaris development machine? What architecture are you targeting (RISC, SPARC, Intel, etc). If you can't get hardware to run those, get some virtual machines for your targets and develop there.
Aside from that, you can install modules anywhere you have permissions. See perlfaq8:
How do I keep my own module/library directory?
How do I add the directory my program lives in to the module/library search path?
How do I add a directory to my include path (#INC) at runtime?
I haven't tried this particular feature, but perl2exe says it supports cross platform builds.
Compiling a Perl script with all its dependencies on Windows with Cygwin and running it Solaris is just not going to work.
Now the question is: do you have access to a compiler on that Solaris computer? It's not because you do not have root access that you cannot compile and install Perl modules in your home directory by using:
perl Makefile.PL PREFIX=$HOME
If you have CPAN available on your Solaris system you can set the prefix in the CPAN shell this way:
start the shell perl -MCPAN -e shell;
change the prefix with conf makepl_arg PREFIX=/path/to/your/home/directory
For your script to run, you can either start perl with the -I $HOME command-line switch, e.g.:
perl -I $HOME script.pl
Your other option would be to place this at the begining of your script
use lib $ENV{'HOME'};
Set your environment variable PERLLIB to your personnal Perl lib directory or use the -I command line switch to Perl to indicate it.
If you have access to the HP-UX machine you can compile Expect there and install it in your directory. But cross compilation from Windows to HP-UX is probably much more difficult. You would have to build a GCC cross compiler.
If you have a compiler on each of your systems (and some other tools needed by configure like grep), you should not only be able to compile modules, but you should also be able to build your own perl executables.
You'll want local::lib. Once you've done that, the pure Perl modules should work cross platform, but you'll have to identify and reinstall the compiled modules on the foreign platform. Do the initial install on a real unix, cpan on cygwin is slow.
I've run across this several times on my work systems. We have a base install of Perl 5.8 and I don't have the ability to add modules. Here's the solution I use:
Create a folder called 'lib' in your
project root (ex:
~/projects/MyProject/lib)
Any
modules you download from CPAN
should have a Makefile as well as a
directory called "lib". Copy the contents of the lib folder into your newly created lib folder. Some modules may only contain a single .pm file, and no lib structure. Just copy the .pm file.
Your code should do the following: first, use any modules that have been installed normally, then unshift your #INC environment variable to use your local libraries:
# Declare Includes --------------------------------------------------------------------------------
use Getopt::Long;
use vars qw($VERSION);
use DirHandle;
use FileHandle;
# Force perl to use our local 'lib' directory for imported modules, this allows us to
# use modules without having to install them in th emain perl assembly. However, this
#also prevents these modules from being used in other projects.
BEGIN { unshift #INC, "lib"; }
use Error qw(:try);
use SOAP::Transport::HTTP;
#use LWP::Protocol::https;
use XML::Simple;
use XML::Writer;
use XML::Writer::String;
The caveat to this method is that some Perl modules don't use the 'lib' method or have additional dependencies. If you run into problems, examine the Makefile.PL for the module and see what it's doing.