Wednesday, December 1, 2010

Packaging Software with RPM/How to build the RPM Package


Packaging Software with RPM/How to build the RPM Package
The principal benefit of open source software is, as its name implies, access to the inner workings of an application. Given the source, you can study how an application works; change, improve, and extend its operation; borrow and repurpose code (per the limits of the application's license); and port the application to novel and emergent platforms.
In general, a pre-built, open source application is called a package and bundles all the binary, data, and configuration files required to run the application. A package also includes all the steps required to deploy the application on a system, typically in the form of a script. The script might generate data, start and stop system services, or manipulate files and directories. A script might also perform operations to upgrade existing software to a new version.
We will go with a example to build a “wget” rpm package
1.      Get the source code for wget
2.       $ tar xzf wget-latest.tar.gz
$ cd wget-1.12
$ ./configure
configure: configuring for GNU Wget 1.12
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... build-aux/install-sh -c -d
checking for gawk... no
$ make
$ sudo make install
./configure queries the system and sets compilation options suitable for the hardware and software detected. make compiles the code, and sudo make install installs the code in system directories. By default, the directories are rooted at /usr/local, although you can change the target root with the --prefix=/some/full/path/name option to ./configure.
To convert this process to RPM, you place the source in a repository and write a configuration file to dictate where to find the source to be compiled and how to build and install the code. The configuration file, called a spec file, is the input to a utility called rpmbuild. The spec file and the binaries are packaged by rpmbuild into an RPM. When another user downloads your RPM, the rpm utility reads the spec file and installs the package per your prewritten instructions.
Building the RPM
To build an RPM, you must:
  • Set up a directory hierarchy per the rpmbuild specifications.
  • Place your source code and supplemental files in the proper locations in the hierarchy.
  • Create your spec file.
  • Build the RPM. You can optionally build a source RPM to share your source code with others.
To begin, build the hierarchy. In a directory in your home directory—say, $HOME/mywget—create five subdirectories:
  • BUILD. BUILD is used as scratch space to actually compile the software.
  • RPMS. RPMS contains the binary RPM that rpmbuild builds.
  • SOURCES. SOURCES is for source code.
  • SPECS. SPECS contains your spec file or files—one spec file per RPM you want to build.
  • SRPMS. SRPMS contains the source RPM built during the process.
At a minimum, you need source code in SOURCES and a spec file in SPECS.
Copying your source
$ cd ~
$ mkdir mywget
$ cd mywget 
$ mkdir BUILD RPMS SOURCES SPECS SRPMS
$ cd SOURCES
$ cp wget-latest.tar.gz .
$ mv wget-latest.tar.gz wget-1.12.tar.gz
$ cd ..

Next, create the spec file. A spec file is nothing more than a text file with a special syntax.Sample spec file as follows: 
# This is a sample spec file for wget
%define _topdir                              /home/strike/mywget
%define name                                                      wget 
%define release                               1
%define version          1.12
%define buildroot %{_topdir}/%{name}-%{version}-root
BuildRoot:                    %{buildroot}
Summary:                                         GNU wget
License:                         GPL
Name:                                                %{name}
Version:                         %{version}
Release:                         %{release}
Source:                           %{name}-%{version}.tar.gz
Prefix:                            /usr
Group:                                               Development/Tools
%description
The GNU wget program downloads files from the Internet using the command-line.
%prep
%setup -q
%build
./configure
make
%install
make install prefix=$RPM_BUILD_ROOT/usr
%files
%defattr(-,root,root)
/usr/local/bin/wget
%doc %attr(0444,root,root) /usr/local/share/man/man1/wget.1
Let's walk through the spec file from top to bottom. Lines 1-5 define a set of convenience variables used throughout the rest of the file. Lines 7-15 set a number of required parameters using the form parameter: value. As you can see in line 7 and elsewhere, variables can be evaluated and combined to produce the value of a setting.
The parameter names are largely self-evident, but BuildRoot merits some explanation to differentiate it from the BUILD directory you already created. BuildRoot is a proxy for the final installation directory. In other words, if wget is ultimately installed in /usr/local/bin/wget and other subdirectories in /usr/local, such as /usr/local/man for documentation, BuildRoot stands in for /usr/local during the RPM build process. Once you set BuildRoot, you can access its value using the RPM_BUILD_ROOT environment variable. You should always set BuildRoot in your spec file and check the contents of that directory to verify what is going to be installed by the package.
Here are a few tips:
  • Do not use ./configure --prefix=$RPM_BUILD_ROOT. This command builds the entire package, assuming that the final location of the files is the build root. It is likely that this would cause any program that needs to locate its installed files at run time to fail, because when your RPM is finally installed on a user's system, the files aren't under the build root anymore—that's just a temporary directory on your build system.
  • Do not include a path in the definition of Source.
  • Version and Release are especially important. Each time you change your application's code or data and make a new RPM available, be sure to increment the values of Version and Release to reflect major and minor changes, respectively. You may find it helpful to bump the release number each time you build an RPM, even if for your own use, to keep attempts separate.
The next section starts with %description. You should provide a concise but clear description of the software here. This line is shown whenever a user runs rpm -qi to query the RPM database. You can explain what the package does, describe any warnings or additional configuration instructions, and more.
The %prep, %build, and %install sections are next, consecutively. Each section generates a shell script that is embedded into the RPM and run subsequently as part of the installation. %prep readies the source code, such as unpacking the tarball. Here, %setup -q is a %prep macro to automatically unpack the tarball named in Source.
The instructions in the %build section should look familiar. They are identical to the steps you used to configure and launch the build manually. The %install section is identical, too. However, while the target of the manual build was the actual /usr/local directory of your system, the target of the %install instruction is ~/mywget/BUILD.
%files lists the files that should be bundled into the RPM and optionally sets permissions and other information. Within %files, you can use the %defattr macro to define the default permissions, owner, and group of files in the RPM; in this example, %defattr(-,root,root) installs all the files owned by root, using whatever permissions found when RPM bundled them up from the build system.
You can include multiple files per line in %files. You can tag files by adding %doc or %config to the line. %doc tells RPM that the file is a documentation file, so that if a user installs the package using --excludedocs, the file is not be installed. %config tells RPM that this is a configuration file. During upgrades, RPM will attempt to avoid overwriting a user's carefully modified configuration with an RPM-packaged default configuration file.
Be aware that if you list a directory name under %files, RPM includes every file under that directory.
Revving the RPM
Now that your files are in place and your spec file is defined, you are ready to build the actual RPM file. To build it, use the aptly named rpmbuild utility:
$ rpmbuild -v -bb --clean SPECS/wget.spec

This command uses the named spec file to build a binary package (-bb for "build binary") with verbose output (-v). The build utility removes the build tree after the packages are made (--clean). If you also wanted to build the source RPM, specify -ba ("build all") instead of -bb. (See the rpmbuild man page for a complete list of options.)
rpmbuild performs these steps:
  • Reads and parses the wget.spec file.
  • Runs the %prep section to unpack the source code into a temporary directory. Here, the temporary directory is BUILD.
  • Runs the %build section to compile the code.
  • Runs the %install section to install the code into directories on the build machine.
  • Reads the list of files from the %files section, gathers them up, and creates a binary RPM (and source RPM files, if you elect).
If you examine your $HOME/mywget directory, you should find a new directory named wget-1.12-root. This directory is the proxy for the target destination. You should also find a new directory named RPMS/i386, which should in turn contain your RPM, named wget-1.12-1.i386.rpm. The name of the RPM reflects that this is wget version 1.12 for the i386 processor.
Verifying the RPM Contents
$ rpm -Vp RPMS/i386/wget-1.12-1.i386.rpm
missing     /usr/local/bin/wget
.M....G.    /usr/local/etc
missing   c /usr/local/etc/wgetrc
.M....G.    /usr/local/share
missing     /usr/local/share/info
missing   d /usr/local/share/info/wget.info
missing     /usr/local/share/locale
missing     /usr/local/share/locale/be
missing     /usr/local/share/locale/be/LC_MESSAGES
missing   d /usr/local/share/locale/be/LC_MESSAGES/wget.mo

The command rpm -Vp RPMS/i386/wget-1.12-1.i386.rpm verifies the package against the files on the system. Although there are seemingly lots of errors, each is a clue that the contents of the RPM file are correct. If you are expecting a file to be installed and it does not appear in the output, it was not included in the package. In that event, review the spec file and make sure the file is enumerated in the %files section.
Another Example : How to Create Binary RPM Package
There is one important rule the reader should take in mind always - never run "rpmbuild" as root. This might end up in an empty root directory sooner as the reader might expect. Therefore the first configuration step to use "rpmbuild" should always be to create a suitable environment in normal user space. We are using the "sandbox" folder in our "Projects" tree for this prupose:
cd ~
mkdir -p Projects/sandbox
cd Projects/sandbox
mkdir SPECS
mkdir SOURCES
mkdir BUILD
mkdir RPMS
mkdir SRPMS
Afterwards we create the file ".rpmmacros" in our home directory:
cd ~
vi .rpmmacros
After the editor launced, please enter the following text according to local environment constraints:
%_topdir /home/$user/Projects/sandbox
%_builddir %{_topdir}/BUILD
%_rpmdir %{_topdir}/RPMS
%_sourcedir %{_topdir}/SOURCES
%_specdir %{_topdir}/SPECS
%_srcrpmdir %{_topdir}/SRPMS



The Spec File
"rpmbuild" needs a ".spec" file to build a ".rpm" file. Create such a spec file in your project directory (where the makefile is residing).
Summary: Key info on software
Name: name-of-the-package
Version: 1.2.3.4
Release: 1
Copyright: Copyright info
Group: Applications/System
%description
Brief description of software package.
%prep
%build
%install
%clean
%files
%defattr(-,root,root)
%doc
/opt/target/bin/programfile
/opt/target/bin/libshared.so
/opt/target/etc/config.cfg
%changelog
Finally – Get the RPM
rpmbuild -bb specfile.spec
Another Example: How to build RPM package
Creating a RPM for your own software requires three basic steps:
  1. Making sure the rpm-build software is installed.
  2. Perhaps modifying your software install process to be rpm "aware".
  3. Writing a spec file.
This last step is the heart of the matter.  The "spec" file tells rpm-build everything that it needs to know to make the software and install it.
The rpm build process basically works as any software build works.  First it compiles the software, then it installs it.  The difference is that rpmbuild generally installs files in a subdirectories of a temporary directory - that way it doesn't interfere with software already installed and it is easy to identify the files that belong to the software package.  After the software is "installed", rpm-build compresses all the files in the subdirectory into one file, and the information in the spec file is included.  This way, when you go to really install the resulting binary rpm, the files will end up in the correct place and rpm knows which files are part of the package.
First you will want to create the following directories:
$ mkdir ~/rpm ~/rpm/BUILD ~/rpm/RPMS ~/rpm/RPMS/i386 ~/rpm/RPMS/i686 ~/rpm/RPMS/noarch ~/rpm/SOURCES ~/rpm/SPECS ~/rpm/SRPMS ~/rpm/tmp

These are the directories where the build process will take place.  You might have to add more directories if your system architecture is different from i386 or i686.

Then you need to create a file ~/.rpmmacros with at least the following lines:
%_topdir               /home/rpmbuild/rpm
%_tmppath              /home/rpmbuild/rpm/tmp

  In a spec file, anything that begins with a % is a macro.  rpmbuild will expand these.  These macros are typically defined in /usr/lib/rpm/macros and nearby files.
  Some macros are particularly important as they break the installation process into sections.  For example, %build tells rpmbuild to begin generating a simple bash script that will be run to build the software package.
  If the build process ends in an error, you can take a look at the bash scripts in ~/rpm/tmp/rpm-tmp.*.  Looking at these scripts will tell you exactly what variables are being defined and what is being done.  Often times you can fix an error in the spec file just by looking at the scripts.
  These scripts will be deleted upon termination of a section, preventing you from looking at them.  You can cheat and get a look at them by forcing an error in that section.  Just add "exit 1" to your spec file in the appropriate section.  Another useful thing is to "set; exit 1" in a spec file.  This will exit and spit out a list of defined environment variables.  Finally, you can place "bash -i" in a spec file to stop rpmbuild and drop to a shell where you can take a look around.
  Use %config(noreplace) - this will save configuration files from being replaced or over-written on update
Yet another Example : Howto Create RPM
First of all, we need to create a directory structure which is going to host the different elements that are required when building an RPM. As a normal user, type:
$ mkdir ~/packages
$ cd ~/packages
$ mkdir BUILD RPMS SOURCES SPECS SRPMS
The different directories will be used for:
  • BUILD : The place where rpmbuild will build the package
  • RPMS : The binary package created by rpmbuild will be stored here
  • SOURCES : The place where to keep the original software sources
  • SPECS : This is where the .spec will be stored
  • SRPMS : The source rpm from your build will be stored there
# yum install rpm-build
In the first place, we need to get the software source available on our system. As said earlier, the directory to host the sources is: SOURCES ;), therefore we are going to cd to this directory and download the source there.
Spec file
$ cd ~/packages/SPECS
$ vi subtitleeditor.spec
Summary: Subtitle editor
Name: subtitleeditor
Version: 0.20.0alpha4
Release: 1.rb7
License: GPL
Group: Applications/Multimedia
URL: http://kitone.free.fr/subtitleeditor/
Source: http://kitone.free.fr/subtitleeditor/files/subtitleeditor-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Patch: gtk_update_icon_cache.patch
BuildRequires: gstreamer-plugins-base-devel, gtk2-devel, gtkmm24-devel, libglademm24-devel
BuildRequires: gstreamer-devel, pcre-devel, libxml++-devel, enchant-devel, iso-codes-devel
Packager: chantra
%description
Subtitle Editor is a GTK+2 tool to edit subtitles for GNU/*. 
It can be used for new subtitles or as a tool to transform, edit,
correct and refine existing subtitle. This program also shows sound 
waves, which makes it easier to synchronise subtitles to voices.
Subtitle Editor is free software released under the GNU General Public License (GPL).
%prep
%setup -q
%patch -p1 -b .buildroot
%build
%configure --disable-debug
%{__make}
%install
%{__rm} -rf %{buildroot}
%makeinstall
%find_lang %{name}
%clean
%{__rm} -rf %{buildroot}
%files -f %{name}.lang
%defattr(-, root, root, 0755)
%doc AUTHORS ChangeLog INSTALL NEWS TODO README COPYING
%{_bindir}/subtitleeditor
%{_datadir}/subtitleeditor/
%{_datadir}/icons/hicolor/*
%exclude %{_datadir}/icons/hicolor/icon-theme.cache
%{_datadir}/applications/subtitleeditor.desktop
%changelog
* Fri Aug 3 2007 - chantra AatT rpm-based DdOoTt org 0.20.alpha4-1.rb
- Initial release.

This file basically contains 7 sections: introduction, prep, build, install, clean, files and changelog. I will go through each section individually.
The first section, up to %prep is pretty explicit by itself. It mainly contains information as seen by rpm -qi subtitleeditor. Here is a short description of each parameters:
  • Summary: a short description of the package
  • Name: the name of the package
  • Version: the version of the package we are building. Here subtitleeditor-0.20.0alpha4
  • Release: the package release number. Here 1.rb7
  • License: the license under which the software is released, here GPL
  • Group: the group your package belongs to. See /usr/share/doc/rpm-/GROUPS
  • URL: software homepage
  • Source: the location of the sources
  • BuildRoot: where the package is going to be built
  • Patch: if you need to apply patch, enter the patch file name and copy the file to the SOURCES directory
  • BuildRequires: packages requires in order to build this packages
  • Packager: the name of the packager
  • %description: a longer description of the package
Prep Section
The prep section prepares the package for the build process. This is the section taking care of untarring the package as well as patching if required.
%setup -q is a rpm macro which knows how to unarchive the source.
%patch will patch the source tree.

Build Section
This section will configure and make the package. Here we use the %configure to which we append the --disable-debug as we don't want to compile this feature.
Then we compile the package with the %{__make} macro.
“The meaning of the macros can be found in /usr/lib/rpm/macros”
Install Section
This section take care of cleaning up the BuildRoot and installing the application.
Clean Section
Cleans up files created by other sections.
Files Sections
defines the files that have to go in the binary RPM. Permission are defined and special attributes can be used.
This section starts with %files, additionally, the list of files that have to be included can be in a separate file, here subtitleeditor.lang.
%defattr(-, root, root, 0755) define the file attributes.
Files can be marked as document files by prepending their name with %doc.
You can exclude a file from a package using the %exclude macro.
Change log Section
You should add a new changelog entry anytime you repackage the RPM. Changelogs entry are defined as:
* Date - email name
- Action taken
Building the package
At this point, in ~/packages we can see the directories:
$ ls ~/packages/

BUILD RPMS SOURCES SPECS SRPMS
SPECS contains subtitleeditor spec file, SOURCES contains our source tarball and eventually, patches.
$ cd ~/packages/SPECS
In order to build a rpm package, We are going to use rpmbuild command
rpmbuild syntax is: rpmbuild -b . So, depending on which build stage you choose, you are going to build either th binary RPM, source RPM, none of them or both of them. Here is the list of the different switches that can be used:
  • -ba : build all, both a binary and source RPM
  • -bb : build only the binary RPM
  • -bc : only compile the program. Does not make the full RPM, stopping just after the %build section
  • -bp : prepare for building a binary RPM, and stop just after the %prep section
  • -bi : create a binary RPM and stop just after the %install section
  • -bl : check the listing of files for the RPM and generate errors if the buildroot is missing any of the files to be installed
  • -bs : build aonly the source RPM
Make sure the ~/.rpmmacros pointing to Correct location
%_topdir /home//packages
$ rpmbuild -ba subtitleeditor.spec (Source rpm and Binary rpm)
$ rpmbuild -bb subtitleeditor.spec (Build Binary RPM)
Signing your pacakage
Signing a RPM with rpmbuild is pretty easy. First, you need to have a GPG key. I suppose that you already have a GPG key. Now, go and edit ~/.rpmmacros and add:
%_signature gpg

%_gpg_path /path/to/.gnupg

%_gpg_name name lastname (comment) 

%_gpgbin /usr/bin/gpg
You might want to use the output of gpg --list-keys to find out we value to enter for %_gpg_name .
$ rpmbuild -ba --sign subtitleeditor.spec

No comments: