Packaging for dummies (2)

After getting hands dirty with DEB packages, now it's time to look how one can start building packages for RPM based distributions like openSUSE.

Case study

We'll return to example from previous post and build package for Mongrel 2. All examples will be presented on openSUSE distribution. You can get one for practice with Vagrant and its community boxes.

Introduction to RPM packages

There are two types of packages:

Single file with .rpm extension and compiled for a particular architecture.

Single file with .src.rpm extension that contains source, patches and control file ending in .spec.

As mentioned above, we have a single .spec file used for describing metadata (description, version, dependencies, changelog, etc.) and orchestrating build process.

Versioning rules are following:

name-version-release.architecture

This translates from upstream mongrel2-1.7.5 to mongrel2-1.7.5-1.x86_64 on 64-bit system and first package revision.

Building package

In order to build package we'll need several tools, rpmbuild is one that should be available on most RPM-based distributions. Besides that there may be helpers available like rpmdevtools backported from Fedora.

Let's prepare environment first - install tools and create directory structure for rpmbuild:

sudo zypper install rpmbuild gcc patch make
sudo zypper -p http://download.opensuse.org/repositories/devel:/tools/openSUSE_12.1 \
  -v install rpmdevtools
rpmdev-setuptree

Few words on just created ~/rpmbuild directory:

Crafting package begins with '.spec' file, let's place one in '~/rpmbuild/SPECS':

rpmdev-newspec ~/rpmbuild/SPECS/mongrel2.spec

Let's examine this template's contents. What you see here is bunch of directives and macros.

Name:           mongrel2
Version:        
Release:        1
Summary:        
License:        
URL:            
Source0:        
BuildRequires:  
Requires:       

%description

%prep
%setup -q

%build
make %{?_smp_mflags}

%install
make install DESTDIR=%{buildroot}

%clean
rm -rf %{buildroot}

%files
%doc

%changelog

While most of them is self-explanatory, examine those:

Enough of templates, it's time to make a proper .spec. After that we'll drop source tarball and a tiny patch for upstream's Makefile (from previous episode) into ~/rpmbuild/SOURCES.

wget http://mongrel2.org/static/downloads/mongrel2-1.7.5.tar.bz2 \
  -O rpmbuild/SOURCES/mongrel2-1.7.5.tar.bz2
cat >> ~/rpmbuild/SOURCES/change_prefix.patch <<EOF
--- Makefile.orig       2012-08-06 01:22:22.274269941 +0200
+++ Makefile    2012-08-06 01:21:47.337270752 +0200
@@ -1,6 +1,6 @@
 CFLAGS=-g -O2 -Wall -Wextra -Isrc -pthread -rdynamic -DNDEBUG $(OPTFLAGS) -D_FILE_OFFSET_BITS=64
 LIBS=-lzmq -ldl -lsqlite3 $(OPTLIBS)
-PREFIX?=/usr/local
+PREFIX?=/usr

 get_objs = $(addsuffix .o,$(basename $(wildcard $(1))))
EOF
mv mongrel2-1.7.5.tar.bz2 change_prefix.patch ~/rpmbuild/SOURCES

Here is final version:

Name:           mongrel2
Version:        1.7.5
Release:        1
License:        BSD 3-Clause
Summary:        Application, language, and network architecture agnostic web server
Url:            http://mongrel2.org
Group:          System Environment/Daemons
Source:         mongrel2-%{version}.tar.bz2
Patch:          change_prefix.patch
BuildRequires:  sqlite-devel >= 3
BuildRequires:  zeromq-devel
BuildRoot:      %{_tmppath}/%{name}-%{version}-build

%description
Mongrel2 is an application, language, and network architecture agnostic web server that focuses on web applications using modern browser technologies.
Mongrel2 supports 17 languages and platforms, HTTP, Flash sockets, WebSockets, Long Polling, and many ways to deploy and hack on it.

%prep
%setup -q
%patch

%build
make all %{?_smp_mflags}

%install
make install DESTDIR=%{buildroot}
mkdir %{buildroot}/%{_libdir}
mv %{buildroot}/%{_prefix}/lib/mongrel2 %{buildroot}/%{_libdir}/mongrel2

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root)
%{_bindir}/mongrel2
%{_bindir}/m2sh
%{_libdir}/mongrel2/config_modules/*.so
%{_libdir}/mongrel2/filters/*.so
%doc LICENSE
%doc docs/
%doc examples/

%changelog
* Sun Aug 05 2012 Firstname Lastname <email> 1.7.5-1
- Initial version

Few highlights on this .spec:

Last thing that deserves mention are that "weird" install instructions:

mkdir %{buildroot}/%{_libdir}
mv %{buildroot}/%{_prefix}/lib/mongrel2 %{buildroot}/%{_libdir}/mongrel2

It exists because on 64-bit RPM-based systems /usr/lib64 should be used instead of /usr/lib. That's also value of %{_libdir} macro on x86_64 used in %files.

Now we're ready to finally build this package we were crafting:

sudo zypper install sqlite-devel zeromq-devel
rpmbuild -ba ~/rpmbuild/SPECS/mongrel2.spec

Compiled package can be found in ~/rpmbuild/RPMS/x86_64/mongrel2-1.7.5-1.x86_64.rpm.

Updating package

Make your fixes by adding more patches, declare them in .spec and lastly bump revision along with changelog entry:

 rpmdev-bumpspec --comment="Summary of changes" \ 
   --userstring="Firstname Lastname <email>"    \
   ~/rpmbuild/SPECS/mongrel2.spec 

That's it! Build it now from that refreshed .spec and grab from ~/rpmbuild/RPMS/x86_64/mongrel2-1.7.5-2.x86_64.rpm.

Further reading

I didn't show how make multiple packages from one spec nor how to use conditionals to make spec reusable between distro-specific values. You can find more on these and other topics in:

What's next?

Now that we've build first DEB and RPM packages it's time to improve them. Stay tuned for next episode and read previous if you haven't done it yet.