The packaging formats used by different distributions of Linux can be a pain point for software developers wishing to release their projects in an easily consumable way. Debian and Ubuntu rely on .deb
packages, while Fedora and RedHat both use .rpm
style packaging systems. These are incompatible and the tools needed to create them can be rather difficult to work with for those unfamiliar with the eccentricities of each.
While package maintainers for distributions do the heavy lifting for packages included in the official repositories, if you plan on releasing software for these distributions on your own site or need to create packages for your organization, you will usually want to provide packages yourself. This traditionally involved learning the workings of at least a few tools for each of the packaging families.
To minimize the complications of this process, a tool called fpm
was created. Using fpm
, you can easily create both .deb
and .rpm
files without having to know any of the commands of the packaging tools that it leverages. In this guide, we will discuss how to use fpm
to create packages of different formats using an Ubuntu 14.04 server.
The main method of distribution for the fpm
tool itself is as a Ruby gem. We can get our system prepared to install fpm
by installing both the Ruby development packages and the software building tools needed to compile additional software.
First, update your local package cache and then proceed with the prerequisite installations:
sudo apt-get update
sudo apt-get install ruby-dev build-essential
Once the above packages are installed, you can acquire the fpm
package itself by typing:
sudo gem install fpm
This will install fpm
on the system level and make it available to any user on the system. Conventional packaging wisdom recommends that you create packages as a normal, non-root user. This is safer in case of a packaging error that may affect your system.
You should now have the fpm
executable available on your system. You can verify this by checking out the tool’s abundant help information:
fpm --help
Intro:
This is fpm version 1.2.0
If you think something is wrong, it's probably a bug! :)
Please file these here: https://github.com/jordansissel/fpm/issues
You can find support on irc (#fpm on freenode irc) or via email with
fpm-users@googlegroups.com
Usage:
fpm [OPTIONS] [ARGS] ...
. . .
You can now get started building packages.
The fpm
tool is fairly good at telling you what it needs to complete a package build. To get an idea of the most basic requirements, you can call the command with no arguments:
fpm
Missing required -s flag. What package source did you want? {:level=>:warn}
Missing required -t flag. What package output did you want? {:level=>:warn}
No parameters given. You need to pass additional command arguments so that I know what you want to build packages from. For example, for '-s dir' you would pass a list of files and directories. For '-s gem' you would pass a one or more gems to package from. As a full example, this will make an rpm of the 'json' rubygem: `fpm -s gem -t rpm json` {:level=>:warn}
Fix the above problems, and you'll be rolling packages in no time! {:level=>:fatal}
This output tells us the basics of what we need to provide to create a package. A typical call will look like this in its basic form:
fpm -s source_type -t target_type source_name_or_location
The source can be any of a number of types. The source type will also dictate the way that the source name or location is passed to the command. We will discuss the possible input and output formats in the next section.
Some of the formats will require additional helper utilities associated with the individual package type to be installed in order to convert. Since we have already installed the Ruby essentials in order to get fpm
working, and since we are using an Ubuntu system, converting a Ruby gem file to a .deb
package should be possible.
Let’s choose a commonly used gem, like bundler
. We can create a .deb
package from the bundler
package located on rubygems.org by typing:
fpm -s gem -t deb bundler
Created package {:path=>"rubygem-bundler_1.6.5_all.deb"}
A file called rubygem-bundler_1.6.5_all.deb
was created in the local directory (your version number may be different). We can now install this as we would any other .deb
package:
sudo dpkg -i rubygem-bundler_1.6.5_all.deb
When you check the list of gems installed, you will see that we now have bundler available:
gem list
*** LOCAL GEMS ***
arr-pm (0.0.9)
backports (3.6.0)
bundler (1.6.5)
cabin (0.6.1)
childprocess (0.5.3)
clamp (0.6.3)
ffi (1.9.3)
fpm (1.2.0)
json (1.8.1)
We could just as easily have used a different source or output format, but these require some additional tools on our system to convert the formats. We will discuss this later.
The fpm
tool can work with quite a few different source and target formats. These each have their own requirements and features.
In the event that there is a relatively standard package index online for a format (such as rubygems.org for Ruby gem files), fpm
is able to automatically search the index and download the necessary files. It will search first for a match in the current directory, and then look in the index if a local match is not found.
The following source types are currently supported:
Source Type | Source Description | How to Pass Source Name or Location | Additional Packages Needed |
---|---|---|---|
dir | Directory or files | Pass the absolute or relative path(s) of the project files on the local filesystem. | (none) |
tar | A tarball source package | Pass the path to the location of the tarball (compressed or uncompressed) on the local filesystem. | (none) |
gem | A Ruby gem | Pass the name of a Ruby gem that can be find on www.rubygems.org. It will be auto-downloaded to create the package. | ruby , rubygems-integration |
python | A Python package | Pass the name of a Python package as you would pass it to easy_install . Names are searched for in the Python Package Index and auto-downloaded to create the package. |
python-setuptools |
pear | A PHP extension | Pass the name of a PHP extension or application found on pear.php.net. The appropriate files will be auto-downloaded when creating the package. | php-pear |
cpan | A Perl module | Pass the name of a Perl module as found at cpan.org. The files will be auto-downloaded and used to build the package. | cpanminus |
zip | A zipped directory structure | Pass the location on the local filesystem of a zip file containing the package. | zip |
npm | A Node.js module | Pass the name of a Node module as specified on npmjs.org. The package will be auto-downloaded and used to make the output package. | npm |
osxpkg | An OS X package | Pass the location on the local filesystem of an OS X package (this will only work on OS X systems with pkgbuild in their path). |
pkgbuild (only available for OS X systems) |
empty | (no source) | Used to create a package without any actual packages. This is used most often for meta-packages that only contain dependencies. | (none) |
deb | A .deb package |
Pass the location of a .deb file on the local filesystem. |
(none on Debian-based systems) |
rpm | An .rpm package |
Pass the location of an .rpm file on the local filesystem. |
rpm |
There are also quite a few options for the target packaging formats that you wish to create. The table below describes some of the different options:
Output Type | Output Description | Additional Packages Needed |
---|---|---|
deb | A Debian-style package that can be installed on Debian or Ubuntu systems. | (none on Debian-based systems) |
rpm | A RedHat-style package that can be installed on CentOS, Fedora, or RedHat systems. | rpm |
zip | A zip file containing the directory and file structure of the input package. | zip |
tar | A tarball (compressed or uncompressed) of the directory structure of the input package. | (none) |
dir | A directory in which to extract the inputted package. | (none) |
sh | A self-extracting .sh file. This is a shell script with a bzipped tar file that will be extracted when run. |
(none) |
osxpkg | A package file for OS X. You must be working on an OS X installation with pkgbuild installed to create these packages. |
pkgbuild (only available for OS X systems) |
solaris | A package suitable for installing on a Solaris system. You must have pkgproto and pkgmk installed, which are only available on Solaris machines. |
pkgproto , pkgmk (only available on Solaris systems) |
pkgin | A package suitable for installing on BSD systems. You must have the pkg_create package installed, which is only available on BSD systems. |
pkg_create (only available on BSD systems) |
puppet | A puppet module that can be used to install on various systems. (Note: This format is not working correctly with the current version of fpm). | (cannot test in non-working state) |
As you can see, some of the formats for both source and target specifications require you to be on a specific operating system. Since we are demonstrating this tool on Ubuntu 14.04, the osxpkg
source format, and the osxpkg
, solaris
, and pkgin
output formats will not be available.
Using the information from the tables in the above section, you should be able to create some basic packages using the fpm
default settings. However, most of the time, you will want to provide some additional information in order to shape the resulting package.
Options should be specified prior to the source arguments that specify the location or name of the original package.
There are many different options available for fpm
. We will only cover a few of the more common ones below. For a full list, check out the fpm --help
command.
Some common options that you may wish to use are:
username@host
for this field.There are also quite a few options that are specific to the packaging formats you select. For a full list, check the help.
If you want to customize more details and do not want a direct translation of one format to your output format, you may need to adopt a different work flow.
This process will mirror traditional packaging a bit more closely, but will also provide the benefits of being able to rapidly produce multiple output formats. We can demonstrate the general workflow below, assuming that the application in question uses a standard ./configure
, make
, make install
compilation and installation process.
To begin, install all of the dependencies that you need for the package. Then, get the source package for the project from its website and place it in a working directory:
mkdir ~/build
cd ~/build
wget http://example.com/project.tar.gz
Now, you can extract the file and change into the resulting directory:
tar xzvf project.tar.gz
cd project
Within this directory, you will have the source files for the application you are wishing to package. You now have the opportunity to make some tweaks to the files and to configure some options.
The normal way to specify options during the build process is to use the included ./configure
script. Check out the project’s documentation to find out which compilation options are available. You can specify them by calling the configure
script with your options:
./configure --compilation_option=value --another_option=value --optional_flag ...
This will create or modify the files that are read by the make
command when the package is being built. We can now create the actual installation files by typing:
make
Instead of installing these files that we’ve configured on our system, we will install them into an empty, dummy directory that we can build a real package out of. Create a directory to install the package to:
mkdir -p /tmp/project
We can label this new directory as the root installation location by passing the DESTDIR
option to make install
:
make DESTDIR=/tmp/project install
Our package has now been cleanly installed to an empty skeleton directory. It has created all of the necessary directory structure, but there is nothing unrelated to the package in that directory.
This is your second opportunity to customize your setup. If you wish to include additional files in the hierarchy for installation, you can add those to the appropriate location within this structure now.
When you are ready, you can use fpm
to create the appropriate package file. For instance, we can pass in some versioning information and a description of the package within our fpm
command. We could also list dependency information or give additional details that will affect the creation of the packaging meta files.
We can use the directory structure to build multiple packaging formats. For instance, we could then create a .deb
package by typing:
fpm -s dir -t deb -C /tmp/project --name project_name --version 1.0.0 --iteration 1 --depends debian_dependency1 --description "A sample package" .
This will create a package called project-name_1.0.0-1_amd64.deb
in the current directory.
You can then modify a few of the options to create an .rpm
package (assuming you installed the rpm
package from the repositories):
fpm -s dir -t rpm -C /tmp/project --name project_name --version 1.0.0 --iteration 1 --depends redhat_dependency1 --description "A sample package" .
This will create a package called project_name-1.0.0-1.x86_64.rpm
in your current directory.
As you can see, it is fairly easy to create customized packages with fpm
. Most of the difficult tasks are still taken care of by the tool.
Using fpm
can make your life easier when attempting to create packages to use across your infrastructure or when distributing publicly downloadable packages of your project. While it might not be the ideal solution for packaging for an actual distribution’s repositories because of policy concerns, its advantages are attractive in many scenarios.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
nice article