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:
- binary
Single file with .rpm
extension and compiled for a particular architecture.
- source
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:
SOURCES
-- contains the original sources, patchesSPECS
-- contains the spec files used to control the build processBUILD
-- directory in which the sources are unpacked, and the software is builtRPMS
-- contains the binary package files created by the build processSRPMS
-- contains the source package files created by the build process
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:
%prep
-- defines script commands to prepare the software before the start of the building process,%setup
is just a macro that unpacks source tarball%build
-- defines script commands to build the source%install
-- defines script commands to install the software, here from temporary build dir into directory expanded from%{buildroot}
macro%clean
-- defines instructions on how to clean out the build root%files
-- list files to be included in a package,%doc
marks them as documentation --rpm
can filter package contents by certain types%changelog
-- lists changes in the package, format is as follows:- Sun Aug 05 2012 Firstname Lastname
1.7.5-1 - Initial version
- Sun Aug 05 2012 Firstname Lastname
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
:
%{name}
,%{version}
,%{buildroot}
resolve to values fromName
,Version
andBuildRoot
directives respectively%patch
macro in%prep
instructs to apply patch from~/rpmbuild/SOURCES/change_prefix.patch
as described inPatch: change_prefix.patch
Group: System Environment/Daemons
is for package category, they're listed in/usr/share/doc/packages/rpm/GROUPS
but you may look for better ones in your distro packaging policy- there are dependencies outlined
%defattr
sets default chmod on package files%{_bindir}
,%{_libdir}
are built-in macros that resolve to directories
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.