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,%setupis 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,%docmarks them as documentation --rpmcan 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,VersionandBuildRootdirectives respectively%patchmacro in%prepinstructs to apply patch from~/rpmbuild/SOURCES/change_prefix.patchas described inPatch: change_prefix.patchGroup: System Environment/Daemonsis for package category, they're listed in/usr/share/doc/packages/rpm/GROUPSbut you may look for better ones in your distro packaging policy- there are dependencies outlined
%defattrsets 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.