NAME

cpantorpm - An RPM packager for perl modules

SYNOPSIS

cpantorpm [OPTIONS] MODULE

This script takes a perl module and creates an RPM for it.

DESCRIPTION

This script automates the entire process of obtaining a perl module and turning it into an RPM package. This includes the steps of obtaining the module distribution, creating an RPM from it, and then making the package available in various ways.

The following steps are involved in this process, and are discussed in more detail below:

Obtain the perl module
Parse various perl modules files for necessary information
Build the package
Generate a spec file
Create the RPM packages
Sign the RPM packages (optional)
Install the RPM (optional)
Store the RPM in a local yum repository (optional)

OPTIONS

General Options

The following general purpose options exist.

-h/--help

Prints a help message describing command usage.

-v/--version

Prints the version of this program.

-D/--debug

Enable verbose debugging output.

-t/--tmpdir DIR

The cpantorpm script makes use of a default directory to store all of it's working files in. It defaults to:

/tmp/cpantorpm

but can be set explicitly with this option.

-f/--optfile FILE

All of the options below that may be specified on the command line may also come from a config file. The config file may contain the options for any number of modules and is described below.

Obtain the perl module Options

The following options affect how a module is downloaded.

-c/--cpan

When downloading modules from CPAN, the script will first try to use CPANPLUS and, if that is not available, it will use CPAN. If this option is included, only CPAN will be tried.

--extracted DIR

Occasionally, the archive file on CPAN is broken in that the archive file (minus the relevent suffixes) is not the same as the archived directory.

For example, the archive Foo-Bar-1.00.tar.gz contains the directory Foo-Bar instead of Foo-Bar-1.00 .

Set DIR to be the name of the directory that it contains.

Module Description Options

Once the module is downloaded, it will be analyzed and various information about the module which will be used in creating the RPM is gathered. This includes looking at the perl META files, the main POD document, and the build scripts (Makefile.PL or Build.PL).

The following options impact these operations:

--name NAME

By default, the name of the package will be obtained from the distribution name. This option can be used to explicitly set the name, overriding the distribution name.

NOTE: the name of the RPM will be based on this, but will typically have a prefix added. See the --prefix and --no-prefix options below for more details.

--summary TEXT

Every package has a 1-line summary description. By default, this comes from the main POD document or the META files, but can be explicitly set using this option.

--description FILE

Every package has a multi-line description. To override the description that comes from the POD document, put the description in a local file, and pass that file name to this option.

--mainpod FILE

The description and summary of the module typically come from the main POD document, if it can be determined using the normal methods described below.

In a few cases (where the POD document is named in some non-standard way), it may not be possible to determine which is the main POD document. In this case, you can specify it using this option.

FILE is the path to the file relative to the top level in the module distribution. For example, it might be:

lib/Foo/Bar.pm
--author AUTHOR

This lists an author for the module, overriding the values from the META files. This option can be included multiple times for multiple authors.

--vers VERSION

This specifies the version of the RPM. It defaults to the version of the package, but can be overridden here.

SPEC File Options

The following options are used during the SPEC file creation step:

-n/--no-tests
--NO-TESTS

When creating a module RPM, typically, the module tests are run as part of the process. These two options can be used to modify this behavior.

The first will add the lines necessary to run the tests to the SPEC file, but (by use of an environment variable), the tests will not be run when the RPM is created. In this instance, if the SPEC file is used to create an RPM at some later date, the tests will run (unless the environment variable RPMBUILD_NOTESTS is set).

With the second option, the lines necessary to run the tests will not be added to the SPEC file at all.

-d/--no-deps
--NO-DEPS

By default, when building an RPM, the prerequisites for the module will be tested.

There are three types of prerequisites:

prerequisites to build the module
prerequisites to run the module tests
prerequisites to use the installed module

It is slightly unfortunate that RPM only recognizes two types. There is no way to specify requirements to run tests.

As such, the build requirements will include those requirements to run the tests unless the --NO-TESTS option is given. In this case, requirements to run the tests will be omitted.

If the --no-deps option is given, dependencies will not be tested (though they will be added to the SPEC file).

If the --NO-DEPS option is given, dependencies will not be added to the SPEC file at all.

In addition, if either of these are given, --no-tests is implied.

--no-compat

By default, the SPEC file includes a line of the form:

Requires: perl(:MODULE_COMPAT_ ...

This is recommended in the fedora packaging guidelines, so is included by default. Some linux distros may not have a version of perl that provides a MODULE_COMPAT, so in these cases, you may need to omit this from the spec. Use this option to do so.

--prefix PREFIX
--no-prefix

By default, a prefix of 'perl-' is added to the name of the package (or the name supplied using the --name option).

To specify that no prefix be added, use the --no-prefix option. To specify an alternate prefix, use the --prefix option.

-p/--packager PACKAGER

Use this option to specify the name of the packager. The name of the packager may be suplied using the '%packager' macro in the ~/.rpmmacros file. If it is not there, this option must be included.

--rpmbuild DIR

RPMs are built in the RPM build hierarchy. This defaults to the value of the '%_topdir' macro, or it can be specified using this option.

If this option is used, the path must be fully specified. A relative path will not work.

The directory will have the following subdirectories:

BUILD
SOURCES
SPECS
SRPMS
RPMS
--clean-macros

By default, macros included in the existing ~/.rpmmacros file will be used. With this option, that file is temporarily removed (it will be restored when the script exits).

--group GROUP

Every package is a member of a group. If this is not specified, it defaults to:

Development/Libraries
--release STRING
--disttag STRING

The full name of an RPM is something like:

foo-bar-1.00-1a-noarch.rpm

The string '1a' here consists of the release (1) and a disttag (a). By default, release is '1' and disttag is the macro '%{?dist}', but these can be overridden with these options.

--epoch EPOCH

This sets an epoch number in the RPM when the version number is not sufficient to determine the relative age of two different versions.

--add-require FEATURE[=VERS]
--add-provide FEATURE[=VERS]

Every RPM has a list of features that are required in order to use it, and a list of features that it provides.

In some cases, you may need to add featurs to these two lists. Both options may include a version:

--add-requires Foo::Bar=0.45
--rem-require FEATURE
--rem-provide FEATURE

Related to the previous options, these options allow you to remove a feature from the requirements list, or the list of features provided.

--repl-require FEATURE=VERS
--repl-provide FEATURE=VERS

Occasionally, the module author explicitly defines the version of a feature that is required, or provided by this module, in a way that breaks things.

These options (which can be included any number of times) will replace the version for either a required feature or a provided feature as they will appear in the spec file. That way, for example, you can make the version of the requirement match what is provided in other RPMs.

For example, if the perl RPM that is installed on the system provides the feature perl=5.24.1 but the module requires perl=5.008001, rpmbuild may fail because it doesn't know how to compare these two version numbers to know whether the requirement is met. When this happens, rpmbuild will fail due to missing dependencies.

In this cae, you could reformat the dependency version by passing in:

--repl-require perl=5.08.1
-m, --macros

Use the macro form of common SPEC constructs over the environment variable form (e.g. %{buildroot} vs $RPM_BUILD_ROOT).

--build-rec, --test-rec, --runtime-rec

Many modules have a list of modules that are recommended to be installed at build time, test time, or at run time, but they are not absolutely required. By default, these modules will not be included as requirements for the various steps. Adding these options will require them.

Module Build Options

The perl module must be built as part of the process. The following options are used during the build:

--build-type TYPE

TYPE must be 'make' or 'build' and specifies that the build must be done using the Makefile.PL or Build.PL files respectively (for those modules that have both). If that file does not exist, an error is triggered.

--config STRING

The given string is passed to either the 'perl Build.PL' or 'perl Makefile.PL' command used to configure the module and create a Build script or a Makefile. This option can be passed in any number of times, but only a single option should be included in each STRING. STRING is passed in as a command line argument:

perl Makefile.PL STRING
perl Build.PL STRING

Since the arguments passed in differ when using a Makefile.PL and a Build.PL procedure, for safety, you should always include the --build-type option when using this option.

--build STRING

Similar to the --config option except this passes strings which are passed to either the './Build' or 'make' command used to actually build the module. This option can be passed in any number of times.

--config-input STRING

Sometimes, the configure step prompts for input that cannot be set using an environment variable. In this case, this option may be used to pass in a single line of input to the 'perl Build.PL' or 'perl Makefile.PL' command used to configure the module and create a Build script or a Makefile. This option can be passed in any number of times, but each value should include a single line of input. This will result in the following:

echo STRING | perl Makefile.PL
echo STRING | perl Build.PL

Since the arguments passed in differ when using a Makefile.PL and a Build.PL procedure, for safety, you should always include the --build-type option when using this option.

--build-input STRING

Similar to the --config-input option except this passes strings which are passed to either the './Build' or 'make' command used to actually build the module. This option can be passed in any number of times.

-T/--install-type TYPE
-i/--install-base DIR

These options allow you to specify where the module will be installed. By default, the module will be built to install in the standard perl location. In most cases, that would mean installing the module, documentation, and scripts in:

BASEDIR/lib/perl5/PERLVERS
BASEDIR/man
BASEDIR/bin

where BASEDIR is the place where perl is installed (which is typically /usr) and PERLVERS is the version directory (i.e. 5.14.2). To install in /usr/local instead of /usr, just use the option:

--install-base /usr/local

To change the module installation directory (but not the directory of the documentation or scripts) to either the site_perl or vendor_perl location, use:

--install-type site
--install-type vendor

to set the module directory to be:

BASEDIR/lib/perl5/site_perl/PERLVERS
BASEDIR/lib/perl5/vendor_perl/PERLVERS

respectively.

The --install-type value must be one of:

perl  (or core)
site
vendor

and defaults to 'perl'. 'perl' and 'core' are synonyms. If this is passed in, it will override any default value set in the Makefile.PL or Build.PL scripts (so be careful about rebuilding core modules).

--mandir STRING

When specifying a prefix (using the --install-base option), it is necessary to determine where man pages should be installed relative to this directory.

Most of the time, this can be determined automatically, but if your version of perl installs man pages by default in a completely separate location from where it installs libraries, it may not be able to be determined correctly and should be specified using this.

The only time this would happen would be if the man pages were installed in one hierarchy and the libraries in a completely different hierarchy (i.e. man pages in /usr and libraries in /opt for example).

--patch FILE
--patch-dir DIR
--script FILE
--script-dir DIR

In a few cases, a distribution cannot be properly packaged unless it is first modified. The modification can be done by applying a patch, or by running a script, or both. Patches are applied first, followed by scripts.

To specify a patch file or script file, use the --patch or --script options. Alternately, you can specify a directory containing files named PACKAGE.sh or PACKAGE.diff where PACKAGE is the string that was passed in on the command line.

By default, no patch or script will be used. They will only be used if one of these options is given.

Scripts and patches will both be applied while in the top directory of the package (i.e. the directory where a Makefile.PL or Build.PL script exists).

The --script-dir option has a second use. If there is a file named PACKAGE.build-sh in it, the lines in that file are added to the SPEC file at the end of the %build step.

Options Controlling Cpantorpm Steps

To control what steps get done, the following options are available:

--spec-only

By default, the script creates a SPEC file, and then builds RPMs (both source and binary).

With the --spec-only option, the SPEC file is created, but no further action is taken.

--no-clean

By default, the build tree will be removed after the RPM is built. If this option is given, it will be left in place.

-s/--sign

If this option is given, a GPG signature will be added to the package.

It should be noted that this step is often interactive, so if the installation process is scripted in any way, adding this option may interfere with the process.

Please refer to the secrtion SIGN THE RPM PACKAGE for more information.

-I/--install
--install-new
--install-force

If any of these options are given, cpantorpm will attempt to install the RPM on the system after it is built. If you are running as root, this will be done by simply running the appropriate rpm command. If you are running as any other user, the command will be run using sudo.

By default, the '-U' flag is given to the rpm command which will cause it to install the RPM if it is a new package, or an upgrade to an existing package.

If the --install-new option is given, the '-i' option will be passed to the rpm command and the RPM will only be installable if it is a new package.

If the --install-force option is used, the flags '-U --force' will be used which will replace an existing package, even if the same version is already installed.

-y/--yum DIR

If this option is given, the RPMs (both binary and source) will be copied to a local yum repository once they are built.

Misc Options

The following misc. options are also available:

--gpg-path PATH
--gpg-name NAME

These options are used to set the path the the GPG directory (which contains the keyring) and the name of the key that will be used.

--gpg-password PASSWORD
--gpg-passfile FILE

When signing a package, this script become interactive unless expect (or perl Expect) is available. If one of these is available, the password can be passed in at the command line (or a file containing the password) using one of these two commands.

--env VAR=VAL

Sets an environment variable before building the package. This option can be used any number of times.

OBTAIN THE PERL MODULE

The perl module may be obtained in a number of different ways. The perl module may exist on local disk either as an archive file or a directory, or it can be retrieved from a URL or from CPAN.

For example, any of the following ways could be used:

cpantorpm Foo::Bar
cpantorpm http://some.host.com/some/path/Foo-Bar-1.00.tar.gz
cpantorpm /tmp/Foo-Bar-1.00.tar.gz
cpantorpm /tmp/Foo-Bar-1.00

When working with a CPAN module, you must use the form:

Foo::Bar

instead of a file form:

Foo-Bar
Foo-Bar-1.00
Foo-Bar-1.00.tar.gz

These are not currently supported.

When downloading from a URL, both ftp:// and http:// URLs are supported (though others such as file:// and https:// are not supported at this time).

For this script to work, the perl module must meet a few validity requirements:

Valid name format

The name of the distribution must be of the form:

PACKAGE-VERS

if obtained from a local directory, or

PACKAGE-VERS.EXT

if obtained from an archive (a local file, a URL, or from CPAN). Here VERS is any string which does NOT contain a dash (-). EXT may be any of the following extensions:

.tar
.tar.gz
.tgz
.tar.bz2
.zip
Standard install script

The module must contain either a Build.PL or Makefile.PL script. A module using some other non-standard build procedure cannot be built with this script.

Getting the module in each of the 4 ways requires different system requirements. In general, the script will try several different ways to get the module, and will only fail if all of the different methods fail.

The following system requirements exist for the different ways of obtaining a module:

From a local directory

You must be able to run the system command 'cp -r' (to recursively copy a directory) or be able to load the module File::Copy::Recursive.

From a local file

You must be able to run the system command 'cp' (to copy a file) or be able to load the module File::Copy.

In addition, you must meet additional requirements for working with the different types of archives as described next.

From a URL

To get a module from a URL, you have to have one of the following packages installed:

curl
wget
lynx
links
lftp

or be able to load one of the modules:

LWP::UserAgent
HTTP::Lite

In addition, you must meet additional requirements for working with the different types of archives as described next.

From CPAN

To get a module from CPAN, you must be able to load one of the perl modules:

CPANPLUS::Backend
CPAN

In addition, you must meet additional requirements for working with the different types of archives as described next.

In each case (except for obaining a module from a local directory), once you have obtained the archive, you need to be able to extract it.

To do this, you need to meet the system requirements for the appropriate type of archive:

.tar, .tar.gz, .tgz files

You need to be able to run the system 'tar' command, or be able to load one of the perl modules:

Archive::Extract
Archive::Tar

These modules will make use of other modules to handle .gz or .bz2 compression.

.zip files

You need to be able to run the system 'unzip' command, or be able to load one of the perl modules:

Archive::Extract
Archive::Zip

Once the package is obtained, in some cases it may be necessary to apply patches or run a script in it to fix things that make it not suitable for packaging.

PARSE VARIOUS PERL MODULES FILES FOR NECESSARY INFORMATION

Building an RPM correctly involves getting a great deal of information from the module. We have to know what features are provided by this module, what features are required by the module to run, as well as the description of the module, the author, etc.

This information can be obtained by a number of different files including:

Makefile.PL, Build.PL

Currently, these are only used to determine how the module should be built. Although they typically contain a great deal more information, it is written as perl code and there is no reasonable way to get the information from them.

However, one of the steps done by this script is to actually build a Build script or Makefile (this ensures that the perl module can be correctly built), and information can be extracted from them since they do follow regular formats.

META.json, MYMETA.json

For a description of the type of data stored here, please refer to the CPAN-Meta documentation on CPAN.

In order to interpret a JSON file, you have to be able to load one of the following perl modules:

Parse::CPAN::Meta 1.40
JSON
JSON::XS
JSON::PP
JSON::DWIW

Most of the information can be obtained from a complete JSON file.

META.yml, MYMETA.yml

For a description of the type of data stored here, please refer to the CPAN-Meta documentation on CPAN.

In order to interpret a YAML file, you have to be able to load one of the following perl modules:

YAML
YAML::Syck
YAML::XS
YAML::Tiny

Most of the information can be obtained from a complete YAML file.

Pod file

In most instances, some of the information (primarily the summary and description of the module) must be obtained from a pod document. This will require one of the modules:

Pod::Select
Pod::Simple::TextContent

The script will need to determine which POD file to get this informaion from (the primary POD file for the package). Most of the time, the script is able to determine which file to use, but if it fails, it can be manually specified using the --mainpod option.

BUILD THE PACKAGE

The next step is to actually build the module.

This step is a departure from the way cpanspec and cpan2rpm work. In both of these scripts, the SPEC file contains the procedure for building the perl module, but it is never tested to see if it works.

This has a couple significant advantages:

It ensures that the package builds

A number of perl modules cannot be built automatically because the scripts are interactive. Unfortunately, the RPM build process does not handle this well, so what you end up with is a hanging process that (eventually) you will have to kill by hand. In other cases, the build process fails for other reasons.

When the build process is put in the SPEC file untested, the RPM build process will either fail or hang.

This script avoids many of those problems.

It generates additional meta data

Both cpanspec and cpan2rpm would interpret the Makefile.PL and Build.PL scripts directly to obtain information from them. Since there is no guarantee that these scripts follow any convention, I considered this a very poor option.

By actually building the module, it creates either a Makefile or a _build hierarchy, and these DO follow regular conventions, and information can be obtained from them with a much greater chance of success.

This script actually builds the module to ensure that it can be done. It watches the process to see if it enters a state where it's waiting for user input, and if it does, the process ends and the RPM is not built, and you can then go in and correct the problem (typically by installing some build prerequisite, or supplying a non-standard option to the build process, or in the worst case, by providing a patch to the module source that removes the interactive nature.

GENERATE A SPEC FILE

Much of the process of generating a spec file is taken from the cpanspec package.

The first step in creating a SPEC file is to determine where the RPM build hierachy lives (since that is where the SPEC file will live). This script supports using the standard build hierarchy, or specifying an alternate location.

If the --rpmbuild option is used, it is used to specify the location of the build hierarchy. Otherwise, the standard location will be used. If a location is specified, and if there is a ~/.rpmmacros file present, the ~/.rpmmacros file must not contain the macro %_topdir that is different than the one specified by the --rpmbuild option. If the macro does exist, you can use the --rpm-clean option to specify a clean version of the .rpmmacros file be used.

The SPEC file created by this script does deviate from the recommended form in one respect. The recommended way to handle the list of requirements and the list of features provided by an RPM is to leave out these lists in the SPEC file and allow rpmbuild to generate them automatically. In the SPEC file, you only list changes to the defaults. In other words, you can add features that are required or that the package provides that were not picked up automatically, or you can add lines to the SPEC file to filter out features that you do not want the rpm to depend on or provide.

Unfortunately, even though adding prerequisites and provided features works well, removing them does not work nearly as smoothly. The methods for filtering prerequisites and features does not work well cross platform (attempts that worked for redhat would not work for openSuSE for example).

As a result, I do not let the SPEC file tell rpmbuild to generate these lists. Instead, I generate the lists (using the standard rpm utilities when available, or using an included script when they are not) and explicitly put them in the SPEC file.

CREATE THE RPM PACKAGES

Once the SPEC file is done, the RPM can be created using the standard RPM tool 'rpmbuild'.

It uses the standard RPM file structure and creates both a source RPM and a binary RPM.

SIGN THE RPM PACKAGES

This is an optional step. If can be used to embed a GPG signature in the package.

In order sign a package, you must have a GPG key available. You must have the gpg package installed on your system and you must have at least one GPG key created.

The path to the GPG directory be specified by one of the following:

the value of the --gpg-path option

the value of the %_gpg_path rpm macro

the value determined by gpg using any currently
set environment variables

If no keyring is found, signing is not available.

The key to use is specified by:

the value of the --gpg-name option

the value of the %_gpg_name rpm macro

the only key in the keyring (if the keyring
contains exactly one key)

If the key cannot be uniquely determined, signing is not available.

The rpm command to sign a package is interactive. In order to script everything, it is necessary to use a tool like expect. If such a tool is not available, and if you are signing packages, this script will be interactive. Currently, if the expect program is installed or the perl Expect module is available, signing can be done non-interactively if either the --gpg-passwd or --gpg-passfile options are passed in.

INSTALL THE RPM

This is an optional step.

After the RPM is successfully built, it can be installed on the system. This will be done in one of two ways. If you are running this as root, it will simply use the rpm command. Otherwise, it will use sudo to run the rpm command.

STORE THE RPM IN A LOCAL YUM REPOSITORY

This is an optional step.

If the --yum DIR option is passed in, the RPMs (both source and binary) are copied in to a local yum repository. The repository is stored at DIR and should have the following directories:

RPMS
SRPMS

RPMs will be stored in either the RPMS/<arch> directory (if it exists) or directly in the RPMS directory. <arch> is typicall something like 'noarch' or 'x86_64'.

CONFIG FILE

A config file can be created which sets options on a per-module basis. It can be either a YAML file (ending in .yaml or .yml) or a JSON file (ending in .json).

A sample YAML file is:

---
Crypt::SSLeay:
   - --config=--default

Foo::Bar:
   - --name Foobar

Each line should contain one option of any of the forms:

--opt=val
--opt val
-o    val

If val contains spaces, you should NOT put quotes around it. Use:

--summary This is the summary

instead of:

--summary "This is the summary"

SYSTEM REQUIREMENTS

This script will try to function under many different situations, and it will often try multiple methods to accomplish a task, and many of those methods will be available on any common linux configuration. As such, a rigorous list of system requirements is overly complicated and won't be listed here. In the event that the script fails, it will list the methods tried and you can make sure that one of them will function on your host.

The most common requirements will be listed here. In all probability, if you meet these requirements, this script will run.

Since the most common way to obtain a module will be from CPAN, you will need one of the following modules installed and correctly configured:

CPAN
CPANPLUS

To make sure it's configured, make sure you can run cpan or cpanp at the command line and have it work.

If you will be applying patches to a package, you will need the patch command.

You also need to be able to read both YAML and JSON files included in almost every module. This means that you will need one JSON module installed out of the following:

JSON
JSON::XS
JSON::PP
JSON::DWIW

and one YAML module from the following:

YAML
YAML::Syck
YAML::XS
YAML::Tiny

You will also need to be able to examine POD files using one of the following modules:

Pod::Select
Pod::Simple::TextContent

You'll should have:

Module::Build
Module::Build::Tiny
ExtUtils::MakeMaker

installed in order to build modules that use the Build.PL and Makefile.PL scripts (both Module::Build and Module::Build::Tiny are supported).

In order to build the rpm, you need the rpmbuild program.

This script also relies on the strace program. This is necessary because many Makefile.PL and Build.PL scripts are interactive so when you run them, they hang waiting for input. Unfortunately, I was not able to find any pure perl way to run a program as a child (or in a thread) and monitor it to see if it's still running because it's doing work, or still running because it's waiting on user input. Though somewhat crude, strace can be used to determine that.

In order to sign packages, you must have the gpg program installed, and you must have a key set up to sign with. In order to do this non-interactively, you also need either the Expect module or the expect program installed.

In order to install the package, you either must be running as root, or have the sudo program. The sudo command may be interactive, depending on how you have it set up.

In order to install RPMs in a yum repository, the repository must exist.

HISTORY

This script is based loosely on Erick Calder's cpan2rpm script and Steven Pritchard's cpanspec script. Initially, I set out to modify one or the other of them, but I found that the modifications that I felt necessary were extensive enough that I decided a fresh implementation was both faster and cleaner.

cpan2rpm

cpan2rpm had basically the full functionality that I wanted. It would download a module, write a spec file for it, create an RPM, and then install it. The only functionality that was missing was some simple functionality to add it to a local yum repository. That would have been very simple to add. However, it suffered from several other significant problems.

cpan2rpm is old. It has not been supported since 2003. It has virtually no support for modules built using Build.PL scripts, and adding it would have been quite complicated.

cpan2rpm is also not written as cleanly, or in a style that I'd like to maintain, so it would take a bit of cleaning up to turn it into something I'd want to maintain.

The main problem though is how it gets information from the Makefile.PL script. In order to get all of the information necessary to create a SPEC file, there's a lot of information about the module that needs to be examined. Much of that information is stored in the various META files in any new module distribution. None of that is used in cpan2rpm (which predates most of them), so that would have to be added. However, even with the META files, some information comes from the Makefile.PL (or Build.PL script) such as the default install location.

Since the data is in a script, cpan2rpm tries to be intelligent about extracting the information. It loads in the Makefile.PL script, modifies it (by turning 'exit' into 'return') and evals it. The theory is that by eval'ing it, you end up with the appropriate data structure that you can examine.

The modifications that it makes are completely unjustified though. It makes drastic assumptions about what the Makefile.PL file looks like, and I can think of any number of cases where turning 'exit' into 'return' won't produce the result you want.

As such, cpan2rpm's handling of the Makefile.PL file needed to be replaced entirely (and since that makes up a significant portion of the script, that justified a complete rewrite).

In addition, if the script contained in Makefile.PL is interactive, cpan2rpm hangs silently while trying to eval it, and there is no easy way to determine what is causing it to hang.

cpanspec

cpanspec is much cleaner in most respects. It is well written, handles both Makefile.PL and Build.PL installs, and handles all of the new META files.

However, it has a few other problems.

It was written specifically for redhat distributions (redhat, centos, fedora) and hardcodes some of the redhat specific paths in it. Other RPM based distributions (such as OpenSuSE which I use) use different paths. At the very least, the cpanspec file would need to be modified to add options to override the defaults.

cpanspec also makes assumptions about where you want to install the modules. It will only install in the vendor location of the primary perl installation. If you want to install them anywhere else, you are out of luck.

But the single biggest weakness was how it handles the Makefile.PL and Build.PL scripts. Rather than evaluating the code, cpanspec just opens them and tries to parse information from them.

Again, given that these are perl scripts, the only reliable way to parse them is to actually use the perl interpreter. Although most modern modules include Makefile.PL or Build.PL scripts that follow certain conventions, it is by no means guaranteed, so I was not satisfied with this assumption.

As with cpan2rpm, cpanspec does not deal with interactive installs. It simply shuffles the problem to another location. In this case, once the SPEC file is created, it is necessary to run rpmbuild, and this will hang.

Due to the weaknesses in both of the existing alternatives, I decided a clean rewrite was in order. The goals were:

Beginning-to-end functionality

cpantorpm will download the module from CPAN, create the SPEC file, generate an RPM, install it, and store the RPM in a local YUM repository for other hosts to use.

Cross platform

cpantorpm will work on any RPM based distribution (though this is only tested on redhat and OpenSuSE to date). Also, many of the steps can be done in many different ways, different one of which may be available by default platforms, so most steps will try more than one way to accomplish the task.

For example, to download a module, cpantorpm will use the CPAN module, the CPANPLUS module, and various tools to download via HTTP (such as wget, curl, etc.).

Correctly handle Makefile.PL and Build.PL

The only way to correctly handle these scripts is to actually build the module. The files generated contain all of the information in a standard format, and we can get it without making any assumptions about the format of these scripts.

Handle interactive installs

Many installs are potentially interactive. If you are missing prerequisites, many modules will stop and ask you if you want to install them first.

cpantorpm traps this behavior and allows you to handle it, rather than hanging for an unknown reason.

Note: by 'handle it', it will not try to install them. Rather, the cpantorpm script will let you know what was being asked, and then exit, and at that point, it's up to you to correct the problem. A future version of cpantorpm may include some automatic handling of missing prerequisites which is the primary cause of interactive installs hanging.

All that being said, I have borrowed ideas (and in rare instances, code) freely from cpanspec and cpan2rpm. I'm very grateful to the authors of both cpan2rpm and cpanspec who's work has made mine much easier.

Hopefully, cpantorpm takes the best of both worlds and improves on that.

KNOWN BUGS

None known.

BUGS AND QUESTIONS

If you find a bug in cpantorpm, there are three ways to send it to me. In order of preference, they are:

GitHub

You can submit it as an issue on GitHub. This can be done at the following URL:

https://github.com/SBECK-github/App-CPANtoRPM

This is the preferred method. Please submit problems requests as GitHub issues if at all possible.

Direct email

You are welcome to send it directly to me by email. The email address to use is: sbeck@cpan.org.

Please note that because cpan.org addresses are published, they are used by a lot of spammers and phishers. Please include the word cpantorpm in the subject line of ALL messages sent to my cpan.org address or it will likely be missed.

CPAN Bug Tracking

You can submit it using the CPAN tracking tool. This can be done at the following URL:

http://rt.cpan.org/Public/Dist/Display.html?Name=App-CPANtoRPM

There was discussion of halting this service a while back (though it continues to function), so only use this as a last resort.

Please do not use other means to report bugs (such as forums for a specific OS or Linux distribution) as it is impossible for me to keep up with all of them. These are the current methods that are guaranteed to notify me.

When filing a bug report, please include the following information:

cpantorpm version

Please include the version of cpantorpm you are using. You can get this by running:

cpantorpm -v

If you find any problems with the documentation (errors, typos, or items that are not clear), please send them to me. I welcome any suggestions that will allow me to improve the documentation.

SEE ALSO

cpan2rpm - Erick Calder's script to generate RPMs

cpanspec - Steven Pritchard's script to generate spec files

LICENSE

This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

Sullivan Beck (sbeck@cpan.org)