Developer

rpmproc .spec file

In this five-part series, Vincent Danen has taught you how to create an RPM from scratch. In the last installment, Vincent shows you how to build the actual RPM package, now that most of the hard work is done.


Welcome to part five of “Making RPMs.” In this Daily Drill Down, we’ll learn how to build the RPM, now that we’ve finished working with the .spec file. In the first four parts of this series, we learned how to write the .spec file and deal with a number of different situations, and we looked at some different ways to make good .spec files. Now we’ll apply that information and build the RPM package itself.
Making RPMs, part 1: The .spec file headerMaking RPMs, part 2: Preparing and compilingMaking RPMs, part 3: Installing filesMaking RPMs, part 4: Finishing the .spec file
The RPM build commands
The basics of building RPMs are quite simple. The hardest part is dealing with the .spec file, and once you have that complete, building the RPM itself is a no-brainer. Once you have your completed .spec file, you simply issue a single command to RPM and it will take care of the rest, using the shell scripts written into the .spec file.

As we’ve been learning to build RPM packages, we’ve dealt with the .spec file for rpmproc, a program used to help make building RPM packages easier. To obtain a copy of rpmproc, visit the Freezer Burn Web site. To quickly review, the .spec file should look like this.
%define name rpmproc
%define version 1.2
%define release 1mdk

Name:                        %{name}
Summary:      Perl script to help manage and build RPM packages
Version:         %{version}
Release:        %{release}
Copyright:      GPL
Group:                       Development/Other
URL:             http://www.freezer-burn.org/rpmproc.php3
Source:          %{name}-%{version}.tar.bz2
Requires:       perl-Mail-Sendmail, rpmlint, rpm
BuildArch:      noarch
Buildroot:       %{_tmppath}/%{name}-buildroot
Packager:      Vincent Danen vdanen@linux-mandrake.com

%description
rpmproc is a simple Perl program to help manage and build RPM packages for those who want an easy wrapper to build RPMs. rpmproc supports announcing changelogs to a mailing list using either qmail or sendmail, signing RPMs, uploading to a user-defined "contribs" site, as well as uploading to a local directory (i.e., for systems that also make their RPMs available via FTP).

%prep –q
%setup

%build

%install
mkdir -p $RPM_BUILD_ROOT/usr/bin
install -m755 $RPM_BUILD_DIR/%{name}-%{version}/rpmproc $RPM_BUILD_ROOT/usr/bin

%clean
if [ -d $RPM_BUILD_ROOT ]; then rm -rf $RPM_BUILD_ROOT; fi
rm -rf $RPM_BUILD_DIR/%{name}-%{version}

%files
%defattr(-,root,root)
%doc README COPYING COPYRIGHT
%{_prefix}/bin/rpmproc

%changelog
* Tue Mar 7 2000 Vincent Danen <vdanen@linux-mandrake.com> 1.2-1mdk
-        first public release
Now that the .spec file is complete and is stored in our /usr/src/RPM/SPECS directory as rpmproc.spec (or in the /usr/src/redhat/SPECS directory under Red Hat), we can build the RPM package itself. The first thing to do is to change to the /usr/src/RPM/SPECS directory and then issue the command to build the binary and source RPM packages:
cd /usr/src/RPM/SPECS
rpm -ba rpmproc.spec


The -ba command tells RPM to build all and will build both the binary and source RPM packages. If you watch the screen closely, you’ll see RPM running the various scripts we defined in the .spec file and a number of other tasks specified internally to build the RPM package. Once this is done, you’ll find two files: /usr/src/RPM/SRPMS/rpmproc-1.2-1mdk.src.rpm and /usr/src/RPM/RPMS/noarch/rpmproc-1.2-1mdk.noarch.rpm. The reason a noarch RPM was built as opposed to the default build architecture (determined by your system architecture) is because of the BuildArch: directive in the .spec file. Since there’s nothing to compile for this program, and because it’s a simple Perl program that will run independently of any platform, we have it build a noarch RPM by default.

If the program was a compiled program without a BuildArch: directive, RPM would build an i386 architecture-independent RPM stored in the /usr/src/RPM/RPMS/i386 directory, assuming that the computer was a 386-class computer. On a Pentium-II computer (or i686), the default build architecture would be i686, and the binary RPM would be stored in the /usr/src/RPM/RPMS/i686 directory.

If you’re building an RPM for distribution, you might want to build it for more than one architecture type. If you use the $RPM_OPT_FLAGS macro (see “Making RPMs, part 2: Preparing and compiling”) when building a compiled program, you’ll pass some optimization flags to the C compiler, which will tell the C compiler to compile the program with some architecture-specific flags that optimize the binary programs. Dependent upon which architecture is being built for, you can specify a target architecture on the command line. For example, let’s assume that you’re building a program and you want to make i386, i586, and i686 binaries available, each optimized. Making sure that you use the $RPM_OPT_FLAGS macro, you’d specify the following commands on the command line:
rpm -ba ‘target=i386 file.spec
rpm -bb ‘target=i586 file.spec
rpm -bb ‘target=i686 file.spec


The first command tells RPM to build all, or build both source and binary RPMs, but build for the i386 platform. The second command tells RPM to build binary, or build just the binary RPM. In this case, we target the i586 platform. And the third command also builds just a binary RPM for i686. The reason we call RPM in this fashion, using the -bb command for the last two builds instead of -ba, which is used only for the first build, is quite simple: The first instance builds the source RPM, and the source RPM never changes. All it contains is the .spec file, any source files, patches, icons, and so forth. It never changes, regardless of what platform you build for; therefore, it’s a waste of time to build the exact same source RPM multiple times.

The following commands can be used to build RPM packages:
  • -baBuild all Source and binary packages are built.
  • -bbBuild binary Only binary packages are built.
  • -bsBuild source Only source packages are built.
  • -bpBuild prep Executes the %prep script from the .spec file.
  • -bl Build list Verifies that all files in the %files section exist.
  • -bcBuild compile Executes the %build script from the .spec file.
  • -bi Build install Executes the %install script from the .spec file.

The first three options are the most commonly used, as they result in the creation of an RPM package. The last four options are debugging options and are typically used to ensure that a particular script in the .spec file is written properly or to help troubleshoot one that isn’t. These commands do not build an RPM package; they merely run the scripts. You can go through the RPM build process step-by-step by using the following commands:
rpm -bp file.spec
rpm -bc file.spec
rpm -bi file.spec
rpm -bl file.spec


This allows you to more closely follow what is happening during each stage of the package build and can also help troubleshoot RPM packages that may be causing you problems during the build phase. Each of the previous four commands, when issued, will run all scripts leading up to it. For instance, if you were to issue
rpm -bi file.spec

the %prep, %build, and %install scripts are all run. Obviously, running the -bi command by itself would normally prove not very helpful since nothing is unarchived and nothing is built. However, if you want to dissect each section piece by piece, you can accomplish this by passing the short-circuit command, like this:
rpm -bp file.spec
rpm -bc ‘short-circuit file.spec
rpm -bi ‘short-circuit file.spec
rpm -bl ‘short-circuit file.spec


This will execute each section independently of the other. Using the commands in this order, you can more closely follow exactly what is happening in each section, without having to redo each section (as would be the case in the first illustration).

Automatic macros
RPM also supports some automatic macros that will make building RPMs much easier, especially if you plan to make many RPM packages that will all contain some static information. Take your favorite text editor and edit a file called ~/.rpmmacros. This file contains some macros that you can use to make building RPMs a little easier. If you’re building RPMs as root, this file would be called /root/.rpmmacros.

Here’s an example ~/.rpmmacros file:
%_signature    gpg
%_gpg_name     Vincent Danen vdanen@linux-mandrake.com
%_gpg_path     /root/.gnupg
%distribution     Linux-Mandrake
%vendor           MandrakeSoft
%packager         Vincent Danen vdanen@linux-mandrake.com
%_topdir             /root/RPM
%_tmppath            /root/RPM/tmp


The ~/.rpmmacros file takes the same syntax and options that are present in the /usr/lib/rpm/macros file. The /usr/lib/rpm/macros file is the systemwide macro file, which is installed by default when RPM itself is installed. RPM will first check ~/.rpmmacros and then /usr/lib/rpm/macros when it tries to fill a macro, so ~/.rpmmacros has a higher precedence. Feel free to look in the systemwide macro file to see what some of the defaults are. If you wish to change anything, add the changed entry to your ~/.rpmmacros file to keep the systemwide macro file pristine.

Let’s examine these macros and see what they mean:
  • %_signature defines how the RPMs will be signed. You can sign RPMs with a GnuPG or PGP digital signature, if you wish. This provides one more way of verifying that the RPM is completely intact, beyond md5 checksums and other verification methods. Valid values are gpg, pgp, and none.
  • %_gpg_name defines which digital signature to use if you’re signing with GnuPG. If you’re using PGP, then use the %_pgp_name macro instead. In the above example, we’re signing with the key belonging to the e-mail address mailto:vdanen@linux-mandrake.com.
  • %_gpg_path defines the location of your GnuPG keyrings. If you’re using PGP, then use the %_pgp_path macro instead.
  • %distribution defines the value of the Distribution: header field. If this macro is defined, you need not include the Distribution: header in the .spec file. It will be automatically inserted into the RPM header.
  • %vendor defines the value of the Vendor: header field. It operates under the same premise as the %distribution macro.
  • %packager defines the value of the Packager: header field. It also operates under the same premise as the %distribution and %vendor macros.
  • %_topdir defines an alternate top-level directory for RPM. By default, in Linux Mandrake the top-level directory is /usr/src/RPM, and in Red Hat it is /usr/src/redhat. With this macro, you can change the top-level directory to anything you wish. This is useful if you build RPMs as different users. For example, you may want all RPMs built by root to be stored in the /usr/src/RPM directory structure, but everything built as user joe in the /home/joe/RPM directory structure. This macro allows you to accomplish this. In the above example, the top-level directory is being set to /root/RPM.
  • %_tmppath defines an alternate temporary directory. By default, the temporary path is set to /var/tmp, but with this macro you change RPM’s default behavior in this respect. In the above example, we’re changing the temporary path to /root/RPM/tmp.

If you’re going to change path information, ensure that the directories you’re going to use exist; RPM will complain if they don’t. For example, if you’re going to change the top-level directory to /root/RPM, make sure that you issue something like this on the command line to create the directories:
mkdir -p /root/RPM/{BUILD,SOURCES,SRPMS,SPECS,RPMS/i386,RPMS/i586,RPMS/i686}

This will create all the subdirectories under /root/RPM that are required for RPM to build and store RPM packages.

There are many other macros that can be used and changed using the ~/.rpmmacros file. To get a list of all the available macros, simply browse the systemwide macro file. Most of them are listed there. Be aware, however, that some macros defined on a Linux Mandrake system may not be available on a Red Hat system or a SuSE system, and vice versa. Linux Mandrake is currently taking some very large jumps in making new macros for RPM to make building RPM packages even easier and more flexible.

Signing RPM packages with GnuPG or PGP
As shown by the previously listed macros, you can also sign your packages with a GnuPG or PGP digital signature. The package signing is done at the time the RPM is built so that the signature is actually a part of the RPM package itself. To sign an RPM package with either GnuPG or PGP, as defined by the macros you define, use
rpm -ba ‘sign file.spec

This will sign the RPMs generated from file.spec. If you’ve defined %_gpg_name and %_gpg_path in your ~/.rpmmacros file, GnuPG will be used to sign the package. If you use %_pgp_name and %_pgp_path, PGP will be used to sign the package. When you issue the above command, you’ll be asked for your secret passphrase, and if the passphrase is correct, RPM will continue and sign the generated source and binary packages. If the passphrase is incorrect, RPM will exit without building the packages.

In general, it’s a good idea to use either GnuPG or PGP because they provide an excellent way to protect data integrity, whether in e-mail messages or files. When building RPMs, it’s a good idea to sign your packages as well because it adds another level of package validation. Anyone can download your RPM packages and use RPM to verify that the signature is intact, verifying that they have a good RPM package that hasn’t been tampered with, as long as they have a copy of your GnuPG or PGP public key file.

Checking RPM packages for errors with rpmlint
The rpmlint tool is a powerful python-based set of scripts that is used to check RPM packages for errors. Typically, rpmlint is used against binary RPM packages, and it will report any errors it finds in the package. For example:
cd /usr/src/RPM/SPECS
rpm -ba rpmproc.spec
rpmlint ../RPMS/noarch/rpmlint-1.2-1mdk.noarch.rpm


This series of commands builds the RPM package for rpmproc and then checks it using rpmlint. If there are no errors, rpmlint will output nothing. If there are errors, rpmlint will display them as either errors, which are big problems that need to be fixed, or warnings, which are noncritical errors, like the fact that the RPM is not signed. You can choose to ignore warnings, but you should pay close attention to any errors that rpmlint reports. Fixing those errors will ensure that you have a proper RPM package.

You can obtain rpmlint from Frederic Lepied’s (the author) Web site. I highly recommend that anyone building RPM packages, for any distribution, download and make use of rpmlint.

Conclusion
In this five-part series of Daily Drill Downs, we’ve looked at building RPM packages right from the beginning of the .spec file to generating the actual RPM packages. Along the way, we’ve explored the many facets of building RPM packages and shown how flexible RPM is in creating easy-to-install packages for any RPM-based distribution.

Because of this flexibility, RPM is a package manager that many distributions take advantage of. Don’t be concerned if your first few attempts at building RPM packages fail or seem difficult. Anyone who starts building RPMs becomes more comfortable with them as they continue. If you have a solid understanding of shell scripting, you should have no problem building RPM packages.

Considering the many thousands of programs out there that are currently not packaged in any format other than a Gzipped tarball, more people building RPM packages means more people using those programs. It’s undeniable to say that many people prefer pristine systems. Anyone who installs an RPM-based distribution will most likely choose an RPM package over compiling their own package, simply to keep dependency and easy uninstallation issues resolved. As RPM packages become more and more prevalent, and more RPM-packaged programs become available, users of RPM-based distributions will find a plethora of new programs available to them. Remember, not everyone is comfortable with or knows how to compile their own programs.

If you’re one of those who can easily compile and install your own programs, give building RPM packages a try. Even if you build them initially for yourself to keep your own system pristine, you’ll find that many people will appreciate your efforts.

Happy hacking!
The authors and editors have taken care in preparation of the content contained herein but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

About Vincent Danen

Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.

Editor's Picks

Free Newsletters, In your Inbox