Tutorial

How To Use FPM To Easily Create Packages in Multiple Formats

Published on August 5, 2014
How To Use FPM To Easily Create Packages in Multiple Formats

Introduction

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.

Installing FPM

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.

Getting Familiar with Basic FPM Functionality

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.

Source and Target Formats

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.

Altering FPM Behavior with Options

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:

  • -C: Specify a directory to change to before looking for files.
  • –prefix: A directory path that specifies where the files in the resulting package will be installed to.
  • -p: The package name and path for your package. This can override the filename of the resulting package file.
  • -n: The name you wish to use for the package. This is the name displayed in packaging tools for your platform.
  • -v: The version number you wish to use for your package.
  • –iteration: The release information for the package. The distribution’s name for this number varies, but it is generally a way to track the package version, as opposed to the application’s version.
  • –license: The licensing name for the package. This will include the license type in the meta-data for the package, but will not include the associated license file within the package itself.
  • –category: The category that this package belongs to. This can be used to organize the package within repos.
  • -d: This can be used multiple times to specify the dependencies of the package.
  • –provides: This can be used to specify the system functionality that this package provides. This is generally used when there is more than one choice to fulfill a requirement.
  • –conflicts: Used to specify packages that must not be installed.
  • –replaces: Used to specify packages that should be removed when this package is installed.
  • –config-files: Used to mark files within the package as config files. Generally, package managers will leave these intact when the package is removed.
  • –directories: Mark a directory as being owned by the package.
  • -a: Specify the architecture for the package.
  • -m: Override the package maintainer field. By default this will use username@host for this field.
  • -e: Manually review and edit the spec file prior to building the package. This can be used to tweak any of the defaults that have been used for the package specs.
  • –description: Set the description for the package.
  • –after-install, –before-install, –after-remove, –before-remove: Script files that should be run at the appropriate time.

There are also quite a few options that are specific to the packaging formats you select. For a full list, check the help.

Customizing Packages

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.

Conclusion

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.

Learn more about us


About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


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

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel