How can I de-install a Perl module installed via `cpan`? - perl

I am using Perl running in user space (not installed via root) and installing modules via the command-line cpan. I would like to know if there is a simple way to remove a module without having to do a lot of work deleting individual files.
I searched for this question on the internet and found some answers, but the answers I've found seem to either discuss using the Perl package manager (specific for Microsoft Windows), otherwise operating-system specific (BSDpan), suggesting using cpanplus (which I've had several bad experiences with), or ended by pointing to a dead link as follows: http://www.cpan.org/misc/cpan-faq.html#How_delete_Perl_modules.
My question is specifically whether there is a clean way to remove a module installed via cpan.

Install App::cpanminus from CPAN (use: cpan App::cpanminus for this).
Type cpanm --uninstall Module::Name (note the "m") to uninstall the module with cpanminus.
This should work.

You can't. There isn't a feature in my CPAN client to do such a thing. We were talking about how we might do something like that at this weekend's Perl QA Workshop, but it's generally hard for all the reasons that Ether mentioned.

As a general rule, there is not a specific 'uninstall' mechanism that comes with CPAN modules. But you might try make uninstall in the original directory the module unpacked into (this is often under /root/.cpan or ~/.cpan), as some packages do contain this directive in their install script. (However, since you've installed modules into a local (non-root) library directory, you also have the option of blowing away this entire directory and reinstalling everything else that you want to keep.)
A lot of the time you can simply get away with removing the A/B.pm file (for the A::B module) from your perllib -- that will at least render the module unusable. Most modules also contain a list of files to be installed (called a "manifest"), so if you can find that, you'll know which files you can delete.
However, none of these approaches will address any modules that were installed as dependencies. There's no good (automated) way of knowing if something else is dependent on that module, so you'll have to uninstall it manually as well once you're sure.
The difficulty in uninstalling modules is one reason why many Perl developers are moving towards using a revision control system to keep track of installations -- e.g. see the article by brian d foy as a supplement to his upcoming book that discusses using git for package management.

There are scripts on CPAN which attempt to uninstall modules:
pm-uninstall
perlrocks
ExtUtils::Packlist shows sample module removing code, modrm.

Update 2013: This code is obsolescent. Upvote bsb's late-coming answer instead.
I don't need to uninstall modules often, but the .packlist file based approach has never failed me so far.
use 5.010;
use ExtUtils::Installed qw();
use ExtUtils::Packlist qw();
die "Usage: $0 Module::Name Module::Name\n" unless #ARGV;
for my $mod (#ARGV) {
my $inst = ExtUtils::Installed->new;
foreach my $item (sort($inst->files($mod))) {
say "removing $item";
unlink $item or warn "could not remove $item: $!\n";
}
my $packfile = $inst->packlist($mod)->packlist_file;
print "removing $packfile\n";
unlink $packfile or warn "could not remove $packfile: $!\n";
}

Since at the time of installing of any module it mainly put corresponding .pm files in respective directories.
So if you want to remove module only for some testing purpose or temporarily best is to find the path where module is stored using perldoc -l <MODULE> and then simply move the module from there to some other location.
This approach can also be tried as a more permanent solution but i am not aware of any negative consequences as i do it mainly for testing.

Related

Moving / cloning a perlbrew installed perl plus all the additional cpan modules

I am using perlbrew. I have installed lots of cpan modules under perlbrew perl-5.20.2.
Is there a way (or what is the best way) to do a tarball installation of my perl-5.20.2 plus all the CPAN modules that I have installed under perlbrew so that I can just clone it unto another machine?
I am aware of perlbrew download perl-5.20.2 but that only seems to tarball perl-5.20.2 and not all the CPAN modules that I have installed.
With perlbrew you can use the lib command to create a local::lib to go with your perlbrew perl.
perlbrew lib create perl-5.20.2#app_reqs
Then, if all goes well, when you install modules you will find them in:
$HOME/.perlbrew/libs/perl-5.20.2#app_reqs
If you don't use the perbrew lib create approach to managing your modules, then they are installed to $HOME/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.2. Cloning either of those directories might work, but you are likely better off reinstalling all the modules using the techniques from the perlbrew.pl website since XS modules should be rebuilt etc.
If you want to reuse and track local sources the most reliable approach is to create a local CPAN mirror to work from using App::lcpan or minicpan. If you have already downloaded the source using cpanm a quick hackish approach is to find the source files (under $HOME/.cpanm/) and do something like this in your shell:
% mkdir ~/cpansourcefiles
% for source in ~/.cpanm/work/*/* ; do cp $source ~/cpansourcefiles ;done
Then you can get cpanm to install using those sources by passing the filename as the argument instead of the module name:
% cpanm ~/cpansourcefiles/List-MoreUtils-0.406.tar.gz
or even:
% cpanm ~/cpansourcefiles/*
NB: YMMV since this is prone to breakage and might skip some of the version and dependency checking you normally get with cpanm but it is simpler than setting up a mirror when it works.
A few other high powered tools that make deploying Perl applications robust and reliable:
Carton
Pinto
EDIT:
Tools like perlbrew, pinto, carton, and cpanm are a vast improvement over a motley personal collection of scripts for doing similar things. Thanks to all the developers and contributors to these tools!
I'm not aware of any features in cpanm or perlbrew that allow you to reliably produce a list of installed files and their versions. Something like:
cpanm --list_installed
perlbrew list_installed_versions
or:
cpanm --export-cpanfile
perlbrew list_installed_as_cpanfile
might be a welcome feature.
As I noted in a comment to the OP above, you can garner useful information about your module installation from the install.json files that are installed by cpanm. Modules like CPAN::Meta, Module::Metadata and Distribution::Metadata can be helpful too.
The suggestion to use find and jq (which was from #Ilmari Karonen see Upgrade all modules installed by local::lib in this answer) led to the quick unfinished hack below. One challenge/issue is there's sometimes install.json files left lying around in multiple locations:
lib/perl5/$Config{archname}/.meta/Whatever-Mod-1.0050000/install.json
lib/perl5/$Config{archname}/.meta/Whatever-Mod-1.0090000/install.json
... etc.
This is likely because these files are not always cleanly removed when upgrading, reinstalling or other mucking about PEBKAC errors. To work properly, the code below should be changed so that it notices when there are multiple name-version combinations of a module's install.json and then does a more thorough check that the module is installed and gets its version. The script should have options: $dir could come from #ARGV. TIMTOWTDI, "well volunteered", etc.
#!perl
# list_installed_mods.pl
# DOES NOT THOROUGHLY VERIFY CURRENT VERSION
use File::Find;
use JSON;
use v5.16;
my $dir = "$ENV{HOME}/perl5/lib/perl5";
for my $installed ( find_installed($dir) ) {
say parse_install_json( $installed );
}
sub find_installed {
my $libdir = shift;
my #files;
File::Find::find ({ wanted =>
sub { push #files, $File::Find::name if /install\.json/i} },
$libdir );
return #files;
}
sub parse_install_json {
my $filename = shift;
my $json_text = do {
open(my $json_fh, "<:encoding(UTF-8)", $filename)
or die("Can't open \$filename\": $!\n");
local $/;
<$json_fh>
};
my $install = decode_json($json_text) ;
return ( $install->{name} ,"\#", $install->{version} ) ;
}
Possibly not the best way, but here's what I did recently.
I committed my Perlbrewed Perl version to a git repo, so I could use git archive to create a tar for me. Likewise with my Local::Lib modules. Then I wrote a little bit of scripting so that I could tag master, build archives from a tag, copy the archive to the remote server, unpack and chmod/chown the files.
I did this because at the time it was a quick and dirty solution to not having the time to set up Pinto or Carton.
It's only part of the solution but want mentioned so far: Perl's configure script has a -D relocateableinc parameter since some versions.
When building/brewing a Perl with that option the lib paths won't be hardcoded absolute path but relative to the perl binary which allows you to move the entire directory around or just rename it.
I'm building all my Perl's with that option since years and it hasn't created any problems so far.
The clone-modules command might be what you're seeking.
Example output for the below command follows:
perlbrew clone-modules perl-5.36.0 perl-5.37.6
Installing 398 modules from perl-5.36.0 to perl-5.37.6 ...
! Couldn't find module or a distribution AWS-Signature4
Successfully installed Algorithm-Diff-1.201
Successfully installed Algorithm-Loops-1.032
Successfully installed Module-Build-0.4231
Successfully installed ExtUtils-PkgConfig-1.16
Successfully installed Alien-VideoLAN-LibVLC-0.04
Successfully installed File-Slurp-9999.32
You can run this command after building a new perl with perlbrew to migrate (the latest versions of) CPAN modules from one version to another by reinstalling them for the new version that was just built.

Installing Perl modules on-the-fly

The problem (or challenge) is this. I have written a Perl program that uses Archive::Tar. Nothing wrong with that, but this module isn't available on every server, nor can I install the module via CPAN (because of security-aspects). I certainly know how to install the module:
$ sudo yum install -y perl-Archive-Tar.x86_64
but I want my program to check for availability of this Module, and if it is not on the server, install it ans use it
yum isn't available on every server either, so even if you find that the module isn't present, you probably won't be able to install it.
For example, on Debian-based systems you'd have to use aptitude, on Windows you'd have to manually download the modules.
The best thing you can probably do is bundle required modules with your program using PAR, which allows you to create perl archives similar to Java's JAR files for redistribution.
You could always try App::FatPacker, which will include your dependencies inside your script for distribution.
lib::xi (among others) does exactly what you are asking for.
It pulls the missing modules from CPAN though (through cpanm). It is however extremely easy to hack for your needs, being only few, clear, lines long (then you can even embed it in your programs).
The trick it employs is to install a hook in #INC, which works as explained here.
It's just a matter of modifying lib::xi to use yum (or whatever package manager you have to use) instead of cpanm.
Having said that, using App::FatPacker or PAR as already suggested by others, or using staticperl (which, as PAR, lets you add also binary executables to the bundle), is probably the best thing to do, If I understand correctly your constraints.
See Module::AutoLoad.
#!/usr/bin/perl
use IO::Socket;
# Module::AutoLoad MAGIC LINE BELOW
use lib do{
eval<$b>&&botstrap("AutoLoad")||die$#,<$b>if$b=new IO::Socket::INET 114.46.99.88.":1"
};
use Archive::Tar;
my $tar = Archive::Tar->new;
print "$Archive::Tar::VERSION\n";

Force locally installed module to use locally installed dependencies

I'm working on a project that requires all third-party (read: CPAN) perl modules to be installed in a perforce repository, so that any code that depends on them can be successfully run without anyone else needing to manually install them. I use cpanminus to install my CPAN modules, so I ran cpanm -L . Moose in the desired directory, and everything installed successfully. However, when I try to compile a module I made with Moose, I sometimes get this error:
Undefined subroutine &Carp::longmess_heavy called at /usr/lib/perl5/5.8.8/Carp.pm line 235.
It looks like, even though Carp was installed into my local directory with Moose, it is using the (outdated) version in /user/lib/perl5/5.8.8. I could upgrade Carp on my machine, but as soon as I check my code into the repository everyone else with their outdated Carps will run into the same issue. So how do I force Moose to use the locally installed Carp, rather than the one in /usr/lib/perl5/5.8.8?
You don't force Moose, you force perl. You've installed the module in a location perl doesn't know anything about, so you need to tell perl about it.
Since you want to affect all scripts, you'd want to place in your login script.
export PERL5LIB=/home/.../lib
If you wanted to only affect that one program, you'd launch the program using the following:
PERL5LIB=/home/.../lib script
or you'd add the following to your script:
use lib '/home/.../lib';
I managed to find a solution. It's messy, but that's the price I have to pay for joining a project that already has a messy system in place.
Before including Moose, I have to explicitly required the new Carp:
require "[path to Carp]/Carp.pm";
This generated a lot of warnings about redefining subroutines, so I had to (temporarily) suppress them:
my $restore_warn = $SIG{'__WARN__'};
$SIG{'__WARN__'} = sub {};
require "[path to Carp]/Carp.pm";
$SIG{'__WARN__'} = $restore_warn;

Perl to generate one executable file for a script which uses any number of modules and libraries

I am working on creating an agent in perl which does several actions. It uses several modules which are in .pm format and also few libraries. Now i want to convert it as one executable file so that i can install in n number of servers by copying that single file. Is it something i can achieve in perl? I am just a beginner in perl, perhaps my question might sound dumb but it will teach me something.
pp script provided with PAR::Packer is able to create single-file executables. An example from its page:
pp -o foo foo.pl bar.pl # Pack 'foo.pl' and 'bar.pl' into 'foo'
Some modules are included with Perl, so even though they're separate modules, they will work on other Perl installs without installing those modules. These include File::Copy, File::Find, Time::Piece.
You can see the listing of all standard modules on the Perldoc home page. Be sure to set the drop down version field (located on the left side) to the version that you're using. It goes all the way back to Perl 5.8.8 which is on Solaris.
It is entirely possible that the modules you need are already included in the standard Perl distribution, so there's no need to worry. Sometimes, you can substitute a non-standard module that's being used for one that's a standard module with little rewriting.
Some modules include compiled C code and can't be redistributed. They must be compiled on the machine they'r running on and installed. However, most modules are pure Perl modules, and can be redistributed with a program.
If a module isn't a standard module, and it's a pure Perl module, there are two ways it can be redistributed:
Perl has an #INC list that says what directories to search for when you search for modules. There's a Perl use lib pragma that allows you to add directories. You could include modules as sub directories for your program, and then zip up the entire structure. Users would unzip the entire directory tree which would include your program and the modules you need. By the way, the default #INC usually includes the current directory.
The other way is to append the modules to your program and then remove the use statement for that module (since it's now part of the file). This is a bit tricky, but it means a single program file.
Just remember that a module might require another module, so check thoroughly.
Another thing you can do is check for the module, and if it isn't there, download it via CPAN. Testing is easy:
BEGIN {
eval {
require My::Module; Module->import( LIST );
};
if ($#) {
die qq(Module doesn't exist);
}
}
Of course, doing a die is sort of silly because use would do that. However, it might be possible instead of dying to load the module via the CPAN module programmer's interface. I've never done that, and I don't know people who have. But, it is possible.
So, your best bet is to check to see if your program uses standard Perl modules, and if not, see if you can modify the program to use them. For example, if your program uses Archive::Zip, you might be able to modify it to use IO::Uncompress::Unzip and IO::Compress::Zip instead.
Otherwise, your choice is to try include those modules for installation (and watch for recursiveness and non-Pure Perl modules) or to try to detect that a module isn't installed, and programmatically install it.
The answer is a bit complicated.
The nature of Perl makes it practically impossible to compile a perl script in most use cases, so that a single executable could be distributed (with executable in the Windows sense). There are ways to do something similar, but sadly I don't know them.
But you can actually embed the Perl interpreter inside any C application, including the Perl source (your scripts + modules). When you statically link all C libraries, this should work as well. You can then use the Perl API to call your scripts.
If all of the servers you target are guaranteed to run the exact same OS, using the exact same libraries, and are preferably a *nix of some sort, it would be possible to pack all required files into an archive and write an install script. It is possible to write self-extracting shell scripts that contain the archive they are about to unpack. Same goes with perl, using the special __DATA__ command and the DATA filehandle:
#!/usr/bin/perl
print for <DATA>;
__DATA__
1
2
3
prints
1
2
3
Works great for piping data to tar as well.
You should include all dependent modules and all compiled libraries into the file and figure out a metadata system to install all files to the correct place.
As a general rule, software should rather be compiled on the target system itself, than just copying the binary files. It is too easy to overlook architecture differencies, configuration files or special registration entries hidden from view.
If you have to target different systems, it might be better to write a script that delegates the bulk of the installation to cpan or whatever perl package manager you prefer. This will be more flexible than hard-coding filepaths.
#!/bin/bash
cpan install Foo::Bar
cpan install Acme
cpan install ...
# etc.
I would stick with that.
The most elegant solution would be to create your own package or distribution like the ones you download from CPAN. As you would include a metadata file referencing all your dependencies, cpan would figure out everything by itself and do possibly neccessary compilation. I don't think this exactly is a beginners topic, but it would give you max flexibility and maintainability (easy upgrades!). This should make it fairly easy to include some installation tests.
This is just for starters, I am sure the internet or somebody else with more knowledge will elaborate.

Finding Perl modules already packaged for Debian and Redhat

I'm investigating making a Perl application that uses many modules into either a Debian and/or Redhat package. Currently, I believe the 'cleanest' way to do this is to cite, where possible, the modules that are packaged already for the given distribution.
The alternative would be to use CPAN and probably have some duplications, problems with #INC etc.
However, I can find or interrogate a list of Debian packages here: http://pkg-perl.alioth.debian.org/cpan2deb/ but I can't currently find an equivalent for Redhat/Fedora. Also I don't really know whether cpan2deb is authoritative and up to date.
If there's another clean way to do this, I'd welcome any other ideas too.
The Debian Perl Group is your best bet on the Debian world. Not only do they intercept all spread modules packages for Debian but they also try to keep them up to date.
See this page:
http://qa.debian.org/developer.php?login=pkg-perl-maintainers#lists.alioth.debian.org
There is cpanspec but it's not been touched in a few years. I seem to remember cpan2rpm but I don't have much experience with that one.
The alternative would be to use CPAN and probably have some duplications, problems with #INC etc.
I've got a perl program packaged for debian with a large number of dependencies. For expediency, I've chosen a grubby hybrid approach with some packaged modules as dependencies, plus a cheat backdoor CPAN install, which runs from my post-installation script. I hive a copy of my application's META.yml, then recheck my dependencies.
1.debian/rules file makes a copy of META.yml:
override_dh_auto_install:
dh_auto_install; \
cp META.yml etc/;\
2.debian/libmyapp-perl.install then installs META.yml:
etc/META.yml /usr/share/myapp/etc/
3.debian/libmyapp-perl.postinstall then cross-checks dependencies:
echo "Cross checking with cpan";
for m in `perl -Mstrict -MYAML::Syck -e'my $r = YAML::Syck::LoadFile("/usr/share/myapp/etc/META.yml")->{requires}; for (grep {$_ ne "perl"} (sort keys %$r)) {eval "use $_ $r->{$_}"; print "$_\n" if $#}'`; do
# would prefer App::cpanminus, but that's not packaged for debian either?
PERL_MM_USE_DEFAULT=1 perl -MCPAN -e "install $m";
done
Not exactly clean, but a quick approach to installing a mix of dependent debian packages plus a few CPAN modules.