This is a follow on to our post on Perl module config files
A large number of our servers run CentOS Linux, and like all Redhat based distributions software is installed through RPM. Unfortunately installing perl modules using CPAN has nasty habit of clashing with the base perl install and causing no end of problems.
To work around this we’ve taken to installing all perl modules through RPM. Some modules are easy to find in third party repositories, but our software uses some less common modules such as Net::SMPP.
The best way we’ve found to convert these elusive perl modules into RPMs is through cpan2rpm by Erick Calder, it’s also a very convenient way to make our own software available as an RPM.
Creating an RPM from a CPAN perl module is extremely simple, just pass the module name as the only command line parameter, it will download from CPAN and build the RPM.
[rpmbuild@dev-linux ~]$cpan2rpm Net::SMPP...RPM: /home/rpmbuild/rpm/RPMS/noarch/perl-Net-SMPP-1.03-1.noarch.rpmSRPM: /home/rpmbuild/rpm/SRPMS/perl-Net-SMPP-1.03-1.src.rpm-- Done --[rpmbuild@dev-linux ~]$
[rpmbuild@dev-linux ~]$cpan2rpm Net::SMPP ... RPM: /home/rpmbuild/rpm/RPMS/noarch/perl-Net-SMPP-1.03-1.noarch.rpm SRPM: /home/rpmbuild/rpm/SRPMS/perl-Net-SMPP-1.03-1.src.rpm -- Done -- [rpmbuild@dev-linux ~]$
cpan2rpm however does have it’s limitations so we’ve added a few extra features to our version.
RPM Pre install commands
Commands that need to be run before you install the package, such as creating users. Defined in Build.PL using rpm_pre or passed on the command line as –pre=”my pre-install command”
RPM Post install commands
Commands to run once the package is successfully installed (These don’t run if it fails). Defined in Build.PL using rpm_post or passed on the command line as --post="my post-install command"
Disable auto-generation of the provides list
By default rpmbuild will automatically create a list of all packages it provides, on occasion this is wrong, so we added a flag to disable this automatic list creation. Passed to cpan2rpm on the command line as –no-auto-prov
Support for Module::Build dependencies
cpan2rpm couldn’t wouldn’t correctly create dependency lists for modules build using Module::Build rather than ExtUtils::MakeMaker, we tweaked the script so these are now listed on the RPM. These come from the requires section of Build.PL
Support for the RPM config flag on files
RPM allows files to be flagged as configuration files within the RPM. This flag prevents RPM from overwriting your configuration files when changed by creating the new version as file.rpmnew. By default we’ll flag anything from the etc_files section of Build.PL
Using John’s earlier Build.PL example we can add in these extra features so that it creates it’s own user and then runs a postinstall script once the RPM is installed (this could setup a database or download updated configuration files for example). These rpm_ tag’s aren’t standard Module::Build features but it will ignore unrecognised tags when building.
use 5.008005;use Module::Build;my $build = Module::Build->new (module_name => 'ModuleName',license => 'perl',dist_author => 'A.N. Other <a.n.other@example.com>',dist_version_from => 'lib/ModuleName.pm',etc_files => {'etc/logrotate.d/modulename' => 'etc/logrotate.d/modulename','etc/modulename.conf' => 'etc/modulename.conf'},install_path => { 'etc' => '/etc', 'script' =>
'/usr/local/bin/modulename' },rpm_pre => 'if ! id module_user > /dev/null 2>&1 ; then
useradd -r -c "Module User" -s /sbin/nologin -d
/usr/local/bin/modulename module_user; fi;'rpm_post => '/usr/local/bin/modulename/postinstall_script');$build->add_build_element('etc');$build->create_build_script;
use 5.008005; use Module::Build;my $build = Module::Build->new ( module_name => 'ModuleName', license => 'perl', dist_author => 'A.N. Other <a.n.other@example.com>', dist_version_from => 'lib/ModuleName.pm', etc_files => { 'etc/logrotate.d/modulename' => 'etc/logrotate.d/modulename', 'etc/modulename.conf' => 'etc/modulename.conf' }, install_path => { 'etc' => '/etc', 'script' => '/usr/local/bin/modulename' }, rpm_pre => 'if ! id module_user > /dev/null 2>&1 ; then useradd -r -c "Module User" -s /sbin/nologin -d /usr/local/bin/modulename module_user; fi;' rpm_post => '/usr/local/bin/modulename/postinstall_script' ); $build->add_build_element('etc'); $build->create_build_script;
If you use cpan2rpm regularly I’d recommend creating a .rpmmacros file in your home directory to save you having to pass a few of the options. This also allows you to enable the optional GPG signing of packages. My .rpmmacros file is:
%_topdir /home/rpmbuild/rpm%packager Mediaburst Ltd <rpmbuild@mediaburst.co.uk>%_signature gpg%_gpgbin /usr/bin/gpg%_gpg_path /home/rpmbuild/.gnupg%_gpg_name my_key_name
%_topdir /home/rpmbuild/rpm %packager Mediaburst Ltd <rpmbuild@mediaburst.co.uk> %_signature gpg %_gpgbin /usr/bin/gpg %_gpg_path /home/rpmbuild/.gnupg %_gpg_name my_key_name
If you plan to use cpan2rpm with standard Perl modules from CPAN I recommend you download it directly from the authors site, however I’m also making available our updated version in case you want to use any of these update features.
- Official cpan2rpm release
- Mediaburst cpan2rpm (we’ve added an mb suffix to the executable)
- Patch containing our changes
I’m not the world’s greatest Perl programmer as I normally write C# so if you’ve got any feedback please leave a comment below.