Tuesday, April 12, 2016

Getting Started with RPMs

Red Hat Package Manager (RPM) is a way to package and deploy software on Red Hat variants that’s similar to Advanced Packaging Tool (APT) on Ubuntu variants. To create RPM files, you can use third party tools like Ant or FPM, but with a little bit of effort you can quickly write your own spec files. This paper isn’t intended to be a complete reference on RPM files. There are a couple of great resources out there to get the full scoop on RPMs and I’ve added them at the end of this paper.
To make an RPM you need two things, a spec file and the rpmbuild program. You can get rpm-build easily enough e.g.



$> yum install –y rpm-build rpmdevtools
$> rpmdev-setuptree


The last command will create your rpm build tree e.g.

/home/user/rpmbuild
/home/user/rpmbuild/RPMS
/home/user/rpmbuild/SPECS
/home/user/rpmbuild/BUILD
/home/user/rpmbuild/SOURCES
/home/user/rpmbuild/SRPMS


If you put your tree somewhere else or if you want to be explicit, add an .rpmmacros file to your home directory.

$> echo “%packager First Last 
%vendor Your Company
%_topdir ${HOME}/rpmbuild” > ~/.rpmmacros

Now you have the basic requirements and we can start writing spec files. However, before we dive into the spec file it helps to understand that spec files have macros and that macros can be a single value or a multi-line value. Here is how we define a variable in a spec file:

%define name value

You can conditionally set the variable like so:

%{!?_version: %define _version 1.0.0}


When you use the variable you use you use the %{} syntax e.g.

Version: %{_version}


You can conditionally use a variable e.g.

Release: 1%{?dist}


The %{?dist} syntax means, output the value of “dist” if it’s set, otherwise output nothing. rpmbuild will fail if you reference macros that are not defined. If you want to see what the current value is of a predefined variable you can run the following command:

$> rpm --eval '%{_rpmdir}'


To get a list of all pre-defined variables, see the links at the end. If you want to see everything you can use either of these two commands:

$> rpm --showrc | less
$> rpmbuild --showrc | less


I piped them to less in my example because there’s a lot of information when you run either of those commands. Since we can define variables and show them, if you wanted to play around you can:

$> rpm --define '_some value' --eval '%{_some}'
value


You can also include variables listed in a separate file

%include some.spec.file


When you define variables in a separate file, you can define macros that span multiple lines. Let’s assume you have a set of application specific preparation steps. Create your steps in a separate file e.g. mysteps.spec Once you do that you can define your macro e.g. Now you have a macro “prepsteps” that can be used in any step after it’s defined. Here’s how you can use it in the “prep” phase e.g.

%prep
%prepsteps


Let’s pull some of this together with a simple example spec file:

$> rpmdev-newspec ex.spec
$> cat ex.spec
Name:           ex
Version:      
Release:        1%{?dist}
Summary:      
Group:        
License:      
URL:          
Source0:      
BuildRequires:
Requires:     
%description
 
%prep
%setup -q
 
%build
%configure
make %{?_smp_mflags}
 
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
 
%clean
rm -rf $RPM_BUILD_ROOT
 
%files
%defattr(-,root,root,-)
%doc
%changelog


If you run that with rpmbuild you’re going to get your first taste of errors. Since we’re playing around, let’s just try to run the prep phase.

$> rpmbuild -bp ex.spec
error: line 2: Empty tag: Version:


Unfortunately, rpmbuild only reports the first error and not the rest. Here is what it looks like if we fill it out some more:

Name:           ex
Version:        1.0.0
Release:        1%{?dist}
Summary:        This is an example
Group:          Example group
License:        GNU
URL:            http://example
Source0:        ex-%{version}.tar.gz
%description
 
%prep
%setup -q
 
%build
%configure
make %{?_smp_mflags}
 
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
 
%clean
rm -rf $RPM_BUILD_ROOT
 
%files
%defattr(-,root,root,-)
%doc
%changelog


Building RPMS: http://www.rpm.org/max-rpm/index.html 
Building RPMS: https://fedoraproject.org/wiki/How_to_create_an_RPM_package 
Naming guidelines: http://fedoraproject.org/wiki/Packaging:NamingGuidelines

No comments: