I have a confession to make: I love to package software. It sounds weird but since software packaging became a part of my day to day job here at Novell I have enjoyed using different packaging software. My primary job function for a couple of years was to package and distribute APIs for the Novell Developer Kit, so I've had the opportunity to use several packaging utilities both on Windows* and SUSE Linux. RPM has been my latest challenge; and, while there was a learning curve, I can now build simple packages using RPM.
There is a wonderful script, named build, that the engineering team at SUSE put together. The script is generally referred to as build.rpm (pronounced build dot r-p-m). Build.rpm extends RPM in ways that make it safer to produce builds that are based on pristine build environments. By creating a chrooted environment in which to build your desired packages, Build.rpm ensures you a safe, pristine build environment. The great news is if you know how to build RPM packages, you already know almost everything you need to use this fantastic tool!
Build.rpm is available as part of SLES and SUSE Professional. The discussion and examples contained here refer to SUSE Professional 9.3 (unless otherwise noted).
Before I jump into the specifics of Build.rpm, here's a little history about RPM or RPM Package Manager (one of those handy recursive acronyms). RPM was designed and developed to give users the ability to easily add and remove software from their Linux systems. Now you might think that the auto tools (such as configure, make, and make install) should be sufficient, but Java* developers sometimes require additional tools. Enter RPM. In many ways, RPM is a glorified shell script. In fact, shell scripts are generated and executed during RPM creation and execution. However, RPM also brings something else to the table: the database. By using a database, RPM is able to track dependencies for you and helps users make sure the software they are about to install actually works.
You can build RPM packages by using the rpmbuild utility, which is distributed as part of the RPM package. (See? Even the RPM package is, well, an RPM package.) To use rpmbuild, you must first create a spec file (which is where there's a learning curve). Spec files can be a little confusing, but they are very powerful. I will walk you through a very simple spec file in the following steps. However, to truly understand spec files (and all of RPM), I recommend reading Maximum RPM by Edward C. Bailey. Also, you should review the RPM-HOWTO.
The following spec file defines a package that takes prebuilt binaries and supporting files and installs them to a location on the end user's system. I've briefly outlined each tag and explained what happens in each section, but see the Maximum RPM book for more detailed information.
The first thing to note is that, like bash, the pound (#) symbol introduces a comment and is ignored by rpmbuild.
# Defines a constant that is used later by the spec file.
%define short_name cldap
# REQUIRED summar or a brief description of the package.
Summary: Novell - LDAP Libraries for C Linux
# REQUIRED. You must name your package.
Name: novell-%{short_name}-devel
# REQUIRED. All packages must have a version.
Version: 2004.09.30
# REQUIRED. The release number.
Release: 1
# Another constant.
%define fullname %{name}-%{version}
# REQUIRED. Describes the license under which the package is released.
Copyright: Commercial
# REQUIRED. The software group. A list of groups is provided on SUSE Linux at:
# /usr/share/doc/packages/rpm/GROUPS.
Group: Development/Languages
# REQUIRED. The name of the source tar ball.
Source: %{fullname}.tar.gz
# Location of the project's home page.
URL: http://developer.novell.com/ndk/%{short_name}lin.htm
# Owner of the product.
Vendor: Novell, Inc.
# Packager of the product.
Packager: NDK Team
# Allows you to specify a directory as the root for building and installing the
# new package (from http://www.rpm.org/RPM-HOWTO/build.html).
BuildRoot : %{_tmppath}/%{fullname}
# Boolean that specifies if you want to automatically determine some
# dependencies.
AutoReqProv: no
# REQUIRED. Provides an in-depth description.
%description
LDAP Libraries for C for Linux* enables you to write applications to access,
manage, update, and search for information that is stored in Novell eDirectory®
and other LDAP-aware directories. The LDAP libraries provide access to
eDirectory based on the C LDAP API (which is based on the draft proposed to the
IETF). Portions of the source for this component are based on the Directory SDK
(Version 2.0), which is available from http://www.openldap.org/ OpenLDAP.
Matching source code is also available
http://developer.novell.com/ndk/cldaplin.htm here.
The following sections are covered in greater detail in the Maximum RPM book. In essence the prep, build, install, and clean sections are each run independently of each other. In each section you enter either RPM macros or shell commands.
%prep
# An RPM macro that untars the source archive (defined in the source tag) and
# puts it in the build root for use by later sections.
%setup -n %{fullname}
# This example does not actually build, make, or compile anyting. If these
# actions were required, they would occur in this section.
%build
%install
# Define my destination dir for packaging (not installing).
DESTDIR=$RPM_BUILD_ROOT
# Clean out anything that might already exist.
%{__rm} -rf $DESTDIR
# Create the directory where I want my files to go. I am copying my files to
# $DESTDIR/opt/novell. When the files are installed to the end user's system, I
# want them to be installed at /opt/novell. The $DESTDIR represents the root
# from where all my files are gathered. Usually, this information is stored in a
# variable, but I expressly left the path here for clarity.
mkdir -p $DESTDIR/opt/novell
# Copy all the necessary files to the dir. The pwd at this point is
# /usr/src/packages/BUILD/novell-bns_ldap-devel-2004.10.04.
cp -a * $DESTDIR/opt/novell
To summarize so far, DESTDIR was set up to be the RPM_BUILD_ROOT. In SUSE Professional 9.3, this is /var/tmp/. The tarball was extracted to/usr/src/packages/BUILD/novell-bns_ldap-devel-2004.10.04, and the contents of this location were copied to /var/tmp/novell-bns_ldap-devel-2004.10.04/opt/novell/ (from where they will be packaged below).
# Clean up everything.
%clean
%{__rm} -rf $RPM_BUILD_ROOT
#This is where we define what files are in the package.
%files
# Change all ownerships to root. The user that owns the files probably does not
# exist on the target system.
%defattr(-,root,root)
# Add the directory defined by /opt/novell from the root of
# /var/tmp/novell-bns_ldap-devel-2004.10.04/.
%dir /opt/novell
# Add all files (recursively) in the /opt/novell dir.
/opt/novell/*
I have not been able to find documentation on this, but it seems that the above section starts in the directory /var/tmp/novell-bns_ldap-devel-2004.10.04 and adds all the files that are defined by in the %files section. If you can confirm my suspicion or have any comments, please email me at caitchison@novell.com.
So there you go! An actual working spec file. I have some examples that you can [build-examples.tar.gz download]. You can play with them and see some of the possibilities of RPM and build.rpm.
Now that you have a feel for RPM (and, specifically, rpmbuild), let's take a look at build.rpm. There are a few major differences, but the spec files are almost the same. The major difference is that build.rpm uses a chrooted environment for the build. By default, a directory is created at /var/tmp/build-root, where a jail is set up. Build.rpm then creates an area where the system can chroot. This makes a clean room to ensure that the files that are installed from the system are pristine. The packages used to create this jail should be located at /media/dvd/suse by default.
Now the packager can have confidence that the produced RPMs are built with packages that are distributed only with the official releases.
The only difference in the spec files is that build.rpm spec files contain a BuildRequires tag, which allows you to define what packages (both standard Linux packages and custom packages) are required to build your project. For example, if you were creating a package that built and distributed Java classes, you would need the Java package. The syntax of this line is:
BuildRequires: java2 apache-ant pam-modules gzip perl
Required packages are delineated by spaces. Don't worry about versions because build.rpm resolves version in a very intuitive way (that I'll discuss later in this article). If I were to add the above line into a spec file. build.rpm finds and installs all of the listed packages. There are a number of packages that are installed by default, however, it is best to explicitly list any packages that your build requires in the BuildRequires line, even if they are installed by default.
So, build.rpm actually performs the following tasks:
In addition to creating a safe build environment, build.rpm also allows you to target different SUSE Linux platforms (such as SLES, NLD, and Professional) using the same spec file on any type of SUSE Linux build machine. You can also test the resultant RPMs in the jail that build.rpm created for you.
Build.rpm also provides several command-line parameters that allow you to change the default behaviors and play some tricks with the required RPM package locations. From the man page:
| --clean | Removes the build system and reinitializes it from scratch. |
| --no-init | Skips the build system initialization and starts the build immediately. |
| --rpms path1:path2:path3... | Defines where build can find the SuSE Linux RPMs that are needed to create the build system. This option overrides the BUILD_RPMS environment variable. |
| --arch arch1:arch2:arch3... | Defines what architectures to select from the RPMs. If you don't specify this option, build automatically sets this to a sensible value for your host. |
| --root buildroot | Specifies where the build system is set up. Overrides the BUILD_ROOT enviroment variable. |
| --help | Prints a short help text. |
| --verify | Verifies the files in an existing build system. |
Note the --rpms flag. The packages that are listed in the BuildRequires tag are searched for in the paths provided in the order that those paths are provided. For example, if your invocation of build.rpm was:
build --rpms path1:path2:path3
And your spec file required perl, build.rpm would search for perl on path1, then path2, and then path3 and install the first perl package that it happened across. Using this functionality, you can easily include service packs or custom packages to your project.
In conclusion, build.rpm provides some very powerful additions and improvements on top of rpmbuild, and it makes packaging software even more fun and exciting than it already is.
For more information, see the following resources:
© 2008 Novell, Inc. All Rights Reserved.