How can Install multiple Perl versions without them tripping over each other's XS modules? - perl

I would like to install several different versions of perl in my home directory. I tried using App::perlbrew, but XS modules from one version were causing segfaults in the other version. Is there any way to install multiple versions of perl and have them automatically keep their XS modules separate?

You can install each perl completely separate from any other perl installation. It's binaries and modules will be completely separate from each other. Essentially, when you install each perl you give it its own prefix:
$ ./Configure -des -Dprefix=/usr/local/perls/perl-5.12.1
Everything is installed under that prefix, and all of the programs in the bin/ will use that particular perl. I go into this in more depth in Effective Perl Programming.
From there, I make symlinks in my ~/bin to each of those programs and attach the version number to it, so I have ~/perl5.12.1, perldoc5.12.1, and so on. I don't ever have to choose to have a version in the way that perlbrew wants you to. I write more about this in Make links to per-version tools. in the Effective Perler blog.
You might be able to use local::lib for this, but it's really designed for you to work with one version of Perl and use one personal library directory. You can tell it to use another directory, but at that point it's really not saving you anything over the traditional way.

Related

How is local::lib supposed to handle XS and different versions of Perl?

I love the idea of local::lib. At least, I find it preferable to the default system-wide installs that cpan/perl defualts to in most every way. However, I'm having difficulties with modules that install XS and distribution upgrades to newer Perls. The instructions for local::lib call for adding this line to your .bashrc (or like file)
eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)"
That executes arbitrary shell code, which is fine. You can see the code it executes by running
perl -I$HOME/perl5/lib/perl5 -Mlocal::lib
In my case it returns this code,
PATH="/home/ecarroll/perl5/bin${PATH:+:${PATH}}"; export PATH;
PERL5LIB="/home/ecarroll/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
PERL_LOCAL_LIB_ROOT="/home/ecarroll/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
PERL_MB_OPT="--install_base \"/home/ecarroll/perl5\""; export PERL_MB_OPT;
PERL_MM_OPT="INSTALL_BASE=/home/ecarroll/perl5"; export PERL_MM_OPT;
In the above, we're setting the default options for the two most widely used module build systems,
MB is for Module::Build
MM is for ExtUtils::MakeMaker
We're telling those system to install to /home/ecarroll/perl5. The rest of the command sets
PERL5LIB which is used in setting #INC. The #INC variable in Perl is used to resolve calls with use.
and, PATH which prepends the bin/ directory that local::lib installs to.
So basically, install perl modules in your home directory, and look first for files installed in your home directory.
My question is what happens when you install something that has XS? In my case, I have an XS file,
/home/ecarroll/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/auto/Class/Load/XS/XS.so
This was installed, with local::lib using an earlier version of Perl. I've since ran a Debian dist-upgrade and got a newer version of Perl (v5.36). Now a lot of utilities produce an obtuse error about this, including ones installed through the distro itself like in my case Dist::Zilla dzil which I installed with Debian's libdist-zilla-perl package.
$ dzil
XS.c: loadable library and perl binaries are mismatched (got first handshake key 0xeb00080, needed 0xeb80080)
Which is all a result of this module's XS,
perl -MClass::Load::XS -e1
XS.c: loadable library and perl binaries are mismatched (got first handshake key 0xeb00080, needed 0xeb80080)
This seems like very routine use of local::lib:
Am I using local::lib in the way intended? What else should I be doing?
How is local::lib supposed to handle XS?
Is there a way to make local::lib support multiple versions of Perl? My distro reads and writes the XS it manages to /usr/share/perl/$PERL_VERSION. This means a dist-upgrade leaves all the XS from the old version behind. It would be nice to have local::lib install not to /home/ecarroll/perl5/lib/perl5/ but instead to /home/ecarroll/perl5/lib/perl5.30/? Note the added minor version. Then at least a dist-upgrade wouldn't break the distribution.
Perl doesn't maintain ABI compatibility between versions. (5.34.x -> 5.36.y, for example.) After installing a new version of Perl, you will need to re-install modules that install themselves in arch dirs (XS modules and a few others). Might be easiest and safest to remove (rename) the local lib directory (/home/ecarroll/perl5) and start from scratch.
That's why I avoid using anything but distro-provided modules with distro-provided perl. The package manager ensures you always have compatible builds of the modules.
If that's not good enough, I recommend installing your own builds of perl instead of using local::lib. Tools like perlbrew makes this easy. You'll still have to re-install all the modules when you install a new perl, but you'll get the cleaner "Can't locate" error message if you forget one.
I rarely use local::lib, but I'm also a minimalist when it comes to administration. The more layers you have, the worse things get. As you see, you're own answer (now deleted) proposes even more complexity to deal with complexity that isn't likely appropriate for your situation.
But, local::lib has a way to set the locations of everything. You are trying to use the same location for everything instead of changing the locations based on what you are trying to do. However, even though local::lib lets you do that, you're likely to forget to switch things up.
My solution is simpler. I merely install a perl for whatever I'm doing (or reuse a suitable perl). I don't change the default locations at all. I never have to manage that. I simply call the perl I want:
$ ls -d /usr/local/perls/*
/usr/local/perls/perl-5.10.1 /usr/local/perls/perl-5.24.3 /usr/local/perls/perl-5.32.0
/usr/local/perls/perl-5.12.5 /usr/local/perls/perl-5.26.1 /usr/local/perls/perl-5.32.1
/usr/local/perls/perl-5.14.4 /usr/local/perls/perl-5.26.2 /usr/local/perls/perl-5.34.0
/usr/local/perls/perl-5.16.3 /usr/local/perls/perl-5.28.0 /usr/local/perls/perl-5.34.1
/usr/local/perls/perl-5.18.4 /usr/local/perls/perl-5.30.0 /usr/local/perls/perl-5.35.11
/usr/local/perls/perl-5.20.3 /usr/local/perls/perl-5.30.1 /usr/local/perls/perl-5.36.0
/usr/local/perls/perl-5.22.4 /usr/local/perls/perl-5.30.2 /usr/local/perls/perl-5.8.9
I have various symlinks and stable path adjustments to I use the tool name and the version tag I gave it:
% cpan5.36.0 ...
% cpan5.32.1 ...
People use all sorts of tools such as perlbrew, plenv, and now some other tool that you think might solve it. It's a big red flag if you have to constantly create new tools to deal with the tools that you are using. I'd rather spend my time doing something else.
I stand by my other answer—this is too much complexity and you should reduce that. But, lets work with that constraint. You want to use local::lib.
The problem is that you are trying to force the same set of modules on any particular perl, no matter how it was compiled. As ikegami explained, you can't assume that two different perls, or the same library compiled with different perls, are compatible. The version of the perls does not matter. Two different perl5.36, or any version, can be incompatible with each other.
But local::lib gives you what you need to set the appropriate values. The module itself can supply some starter values:
$ perl -I$HOME/perl5/lib/perl5 -Mlocal::lib
Attempting to create directory /Users/brian/perl5
PATH="/Users/brian/perl5/bin${PATH:+:${PATH}}"; export PATH;
PERL5LIB="/Users/brian/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
PERL_LOCAL_LIB_ROOT="/Users/brian/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
PERL_MB_OPT="--install_base \"/Users/brian/perl5\""; export PERL_MB_OPT;
PERL_MM_OPT="INSTALL_BASE=/Users/brian/perl5"; export PERL_MM_OPT;
But, you don't need to use those values everywhere. You can have as many sets of those variables as you like. You can have a different set for every project, every perl, or however you want to organize it. The only trick is how you select the right set.
The synopsis of local::lib indicates that it expects you to set the appropriate value for your project:
use FindBin;
use local::lib "$FindBin::Bin/../support"; # app-local support library
Knowing that, you can have your per-version libraries by adding $^V in the path you give to local::lib (still with the binary incompatibility problems):
use FindBin;
use local::lib "/my/modules/$^V";

How to Redistribute Non-core Modules in Perl?

I am relatively new to Perl and I need some help with redistributing the non-core modules. Here's the whole story.
There are two non-core modules that were used in the Perl script: XML::Simple and SOAP::Lite. The version that I'm using (currently on Windows) is Strawberry Perl, so these two modules are already included. However, we don't know if the end users (Unix/Linux system) have these two modules as they might only have the standard version and so have only the core modules. My goal is to make the end users do as little configurations/installs as possible.
First, I tried to see if there's any core modules that's similar to XML::Simple and SOAP::Lite. Unfortunately, I didn't find any (Please correct me if I'm wrong).
So I guess now the only option is to redistribute the two modules. I checked and that these two modules allow redistribution. My problem right now is how to do it. I tried googling with keywords "perl redistribute" but didn't find anything useful. My guess is that we use the exporter tool to achieve this. but these two modules are rather complicated modules and they have several nested folders/pm files (and a whole bunch of other files like MAKE, pod, ini files) so I'm not sure what I should do. The examples I found using exporter are rather simple: They only have 1 pm file and 1 pl file and they are placed into one folder.
Also, I'm open to any other better ways to deal with the problem. The goal is just to make sure all end users can use my script with the least configuration/install efforts as we don't want them to run into a whole bunch of compatibility issues.
Any help would be appreciated. Thanks! =D
I want to elaborate a little bit on #ikegami said.
SOAP::Lite has a large number of CPAN dependencies, so the people installing your module are going to need CPAN access to get it to build, whether you provide it for them, or list it as a dependency. Otherwise, you'll need to provide your entire dependency tree, at which point, you end up using perlbrew, maybe carton, possibly local::lib, and then you might decide you need the next higher level and produce RPMs and DEBs.
Probably better to just provide your script, packaged as a CPAN module, list your dependencies within, and let the chips fall where they may.
Just state a dependency on the modules in your Makefile.PL or Build.PL, then give them the following installation instruction:
cpanm script.tar.gz
One of the best things about Perl is The CPAN, the Comprehensive Perl Archive Network. It's a mirroring service that since about the time Perl 5 originally came out has allowed people to share useful add-on modules, like XML::Simple or SOAP::Lite through a standard, common tool, the cpan client that comes with Perl. Almost all Perl distributions (such as Strawberry Perl and most Perl distributions that come with linux) have a CPAN client configured and included with them. This client lets people download and install modules from CPAN simply by knowing the name of the module.
Almost all module distributions on CPAN follow the exact same layout. They usually have a Makefile.PL file (if it uses ExtUtils::MakeMaker to generate the install script), Build.PL file (if it uses Module::Build to generate the install script), or both. These Perl scripts, once run, create a 'Makefile' or a 'Build' file that can let you install the module and verify that all prerequisites are met.
If you've never made a Perl distribution before, you can download any distribution you want from CPAN and take a look at how things are laid out. The folders and file locations are pretty intuitive once you've seen one. They are usually laid out with the install script and supporting files (like a readme) in the root directory, with the custom modules (modules you make) in the lib directory, and with unit tests in the t directory.
I'd recommend looking at the Build.PL based ones if you're a novice; these are pure Perl based install scripts. If you decide to make a Module.PL based distribution, it's really easy to specify that your module distribution needs XML::Simple and SOAP::Lite. First, create a basic Module::Build based install script. This looks something like:
use Module::Build;
my $build = Module::Build->new(
module_name => 'Foo::Bar',
license => 'perl',
requires => {
'perl' => '5.6.1',
'Some::Module' => '1.23',
'Other::Module' => '>= 1.2, != 1.5, < 2.0',
},
);
$build->create_build_script;
(This is taken right from the Module::Build::Authoring docs).
Then, specify the libraries you need and minimum versions of them. Zero (0) is an acceptable version if you don't care, but that means "anything" is good. I'd recommend specifying at least the version of the libraries installed on the machines you're testing with.
(Neat short cut: you can find out the version of any library that has a $VERSION package variable defined by doing:
perl -MSome::Lib -E "say Some::Lib->VERSION()"
.)
To install the module, the steps look something like this:
cd folder\where\my\lib\is
perl Build.PL
Build
Build test
Build install
This will create the install tool, prepare the folder for testing (usually just copying stuff to a build library area for simple modules), run all .t scripts in the t folder (the "tests", which usually use Test::More for unit testing of the module prior to install), and then finally, install your module to your PC's Perl site libraries.
The Build script, as part of the 'setting things up' phase, will look at your prerequisites, and warn you if you don't have them yet.
Then, as pointed out in ikegami's answer, if you use the cpanm client to install your library, the cpan client will automatically go out, download, test, and install your dependencies for you! Alternatively, Build.PL based installers also have the 'installdeps' option, which will do the same thing. Then any and all dependencies (and potentially recursive dependencies) are automatically downloaded, tested, and installed, even if they change in the future.

Is it safe to have various Perl installations on one machine share a cpan_home?

I have a dated system perl 5.8.8 on a Linux machine and installed a 5.12.4 from ActiveState. Both perl's cpan_home is ~root/.cpan. I was about to change the new perl's cpan_home, but then I realized I didn't know whether I really have to do this or whether it would just result in multiple copies of modules being downloaded to different directories when in fact they could be shared.
So can they? Is it safe? With regard to compiling? Or do I have to go for separate cpan_home directories?
Note that I tried perlbrew first but it failed with Can't load '../lib/auto/IO/IO.so' for module IO: ../lib/auto/IO/IO.so: wrong ELF class: ELFCLASS64 at ../lib/XSLoader.pm line 70. So instead of pursueing the issue I went for ActivePerl, which installs easily.
I have a dozen builds of Perl on my system, and they all use ~/.cpan. I have never had a problems, but I cannot say that it is safe. It depends on the settings therein. Specifically,
build_dir_reuse should (probably) be zero.
makepl_arg shouldn't contain INSTALL_BASE.
mbuildpl_arg shouldn't contain --install_base.
"Install base" overrides where the modules are installed. If you start installing the modules for all your builds in one location, you will have problems due to incompatibilities between versions, releases and builds of Perl.
If you want to share .cpan and have a local install directory, you can probably get away with using PREFIX=/home/username/perl5 LIB=/home/username/perl5/lib instead of INSTALL_BASE=/home/username/perl5. It uses a smarter directory structure.
By the way, local::lib causes "install base" to be used, so you'll run into problems if you use local::lib with multiple installs of Perl.

How do I use perlbrew to manage perl installations aimed at web applications?

I have been using perlbrew to manage multiple versions of perl on a Linux Fedora notebook. I have used it with great benefit to run command-line scripts mostly using App::cmd.
I now want to move to running web applications written using CGI::Application using different perls installed in my $HOME. I am familiar with running Perl web applications in $HOMEs using Apache's user_dir or creating Virtual Hosts but I am unable to come up with a clean way of integrating this and the perlbrew managed perls. Specifically I need help in understanding and finding answers to these questions:
How do install mod_perl under perlbrew?
Assuming this is done, how do I configure my VirtualHost so that it picks up the correct perl that is current?
If this is not possible, (which I doubt) can I at least use local installations to run vanilla CGI?
Thank you for your attention.
I don't think this is a good use for perlbrew, which moves around symlinks under its own directory. The trick is switching the mod_perl module around. Remember, mod_perl is going to be binary-incompatible between major versions of perl, and that you will have to compile it against apache for each version of perl (and apache) you want to use.
perlbrew really does two big things for you:
Installs perl, which is trivially easy to do on your own.
Switches around symlinks so perl is whatever version you want.
If you give up on that last one, perlbrew isn't really doing that much for you. I don't think the symlink feature is particularly valuable.
I think perlbrew is fine for what it is, but when you start doing things outside of its limited scope, it's time to not use it. It's supposed to be a tool to save you some time and headache, so if it's not accomplishing that goal, it's not the right tool for your situation.
In this situation, where I'm supporting a single, big web application, I give it its own perl installation that I don't let anything else use.
For your other questions:
markdown placeholder
You shouldn't have to configure any VirtualHost stuff. If you are using mod_perl, perl is already in there and you don't get to choose a perl. If you're using CGI stuff, you specify the perl on the shebang line. You will have to ensure apache picks up the right library directories, but I think perlbrew handles that. You might have to use SetEnv or something similar in your httpd.conf.
For vanilla CGI, just point to the right (symlink) path for whatever the default perlbrew version is. The CGI program will just use whatever perl that path points to.
See brian d foy's answer for why not to expect to use perlbrew to switch between versions of mod_perl. I also expect that you will need to run multiple Apache servers, if you need multiple different Perl versions under mod_perl.
However, using perlbrew as an easy way to build Perl is IMHO a valid thing to do, and there are few instructions available for how to run mod_perl under perlbrew.
First ensure perl is built with shared library support, by passing the -Duseshrplib flag (otherwise on 64-bit systems you will get a confusing build failure about -fPIC):
perlbrew install perl-5.16.3 -Duseshrplib
Install the development Apache libraries for your system. On Debian, this differs depending on the Apache MPM that you are using. For the prefork MPM:
sudo apt-get install apache2-prefork-dev
Or for the worker MPM:
sudo apt-get install apache2-threaded-dev
Then you need some options to build and install mod_perl2 into the right place. Note that this means cpanm will fail to build it, but you could use it to get hold of the source:
cpanm mod_perl2 # fails
cd ~/.cpanm/latest-build/mod_perl-2.0.8/ # adjust mod_perl version
Adjust the version of Perl below accordingly. (The MP_APXS option is to give the right path for Debian-based systems, which you might not need.)
perl Makefile.PL MP_APXS=/usr/bin/apxs2 \
MP_AP_DESTDIR=$HOME/perl5/perlbrew/perls/perl-5.16.3/
make
make install
Finally, change the LoadModule line in your Apache configuration file (adjusting paths accordingly):
LoadModule perl_module <your homedir>/perl5/perlbrew/perls/<your perl>/usr/lib/apache2/modules/mod_perl.so
Your mod_perl installation will now be running the version of Perl that you want. Install all your required CPAN modules and get going.

How do I use a vendor Apache with a self-compiled Perl and mod_perl?

I want to use Apple's or RedHat's built-in Apache but I want to use Perl 5.10 and mod_perl. What's the least intrusive way to accomplish this? I want the advantage of free security patching for the vendor's Apache, dav, php, etc., but I care a lot about which version of Perl I use and what's in my #INC path. I don't mind compiling my own mod_perl.
Build your version of Perl 5.10 following any special instructions from the mod_perl documentation. Tell Perl configurator to install in some non-standard place, like /usr/local/perl/5.10.0
Use the instructions to build a shared library (or dynamic, or .so) mod_perl against your distribution's Apache, but make sure you run the Makefile.PL using your version of perl:
/usr/local/perl/5.10.0/bin/perl Makefile.PL APXS=/usr/bin/apxs
Install and configure mod_perl like normal.
It may be helpful, after step one, to change your path so you don't accidentially get confused about which version of Perl you're using:
export PATH=/usr/local/perl/5.10.0/bin:$PATH
You'll want to look into mod_so
I've done this before. It wasn't pretty, but it worked, especially since vendor perl's are usually 2-3 years old.
I started with making my own perl RPM that installed perl into a different location, like /opt/. This was pretty straight forward. I mostly started with this because I didn't want the system utilities that used perl to break when I upgraded/installed new modules. I had to modify all my scripts to specify #!/opt/bin/perl at the top and sometimes I even played with the path to make sure my perl came first.
Next, I grabbed a mod_perl source RPM and modified it to use my /opt/bin/perl instead of /usr/bin/perl. I don't have access to the changes I made, since it was at a different gig. It took me a bit of playing around to get it.
It did work, but I'm not an RPM wizard, so dependency checking didn't work out so well. For example, I could uninstall my custom RPM and break everything. It wasn't a big deal for me, so I moved on.
I was also mixing RPM's with CPAN installs of modules (did I mention we built our own custom CPAN mirror with our own code?). This was a bit fragile too. Again, I didn't have the resources (ie, time) to figure out how to bend cpan2rpm to use my perl and not cause RPM conflicts.
If I had it all to do again, I would make a custom 5.10 perl RPM and just replace the system perl. Then I would use cpan2rpm to create the RPM packages I needed for my software and compile my own mod_perl RPM.