From Fedora Project Wiki

Documentation Summary:

Purpose: Document to explain how to build and modify packages.

Audience: Experienced users interested in building RPM packages, either for the Fedora Project or their own use.

Assumptions: Reader has root access to a Fedora system.

Related Documents: Maximum RPM; RPM HOWTO

Lead Writer: Ignacio Vazquez-Abrams


RahulSundaram Note to Writer:

There seems to be a related guide available. You might want to combine the work being done

http://koti.welho.com/vskytta/packagers-handbook/packagers-handbook.html

VilleSkyttä notes: packagers-handbook is in docs CVS, see http://cvs.fedora.redhat.com/viewcvs/packagers-handbook/?root=docs


Building Packages in Fedora

Changelog

2005.08.22: 0.0: Initial proposal (Ignacio Vazquez-Abrams)
2005.08.30: 0.1: Moved to wiki (Ignacio Vazquez-Abrams)
2005.09.10: 0.2: Filled in intro to "Creating a New Package" and "Case Study: leafpad" (Ignacio Vazquez-Abrams)
2005.10.31: 0.3: Filled in mock appendix (Ignacio Vazquez-Abrams)
2005.12.30: 0.4: Adjusted the creating a non-root rpmbuild Buildroot (James Lawrence)
2006.03.16: 0.5: Filled in the library section (Ignacio Vazquez-Abrams)
2009.03.30: 0.6: Pruned empty sections (Ignacio Vazquez-Abrams)

Introduction

RPM (RPM Package Manager) is at the heart of Fedora. It is responsible for installing new software, as well as tracking files so that the same software can be uninstalled with a minimum of fuss. But RPM on its own doesn't actually do anything. It's up to the packager to specify how a package is built, as well as explaining what sort of files the package contains, and what other packages it relies upon for proper operation.

Goals

This guide explains how to create packages under Fedora as opposed to other RPM-based Linux distributions. It is not intended to be an exhaustive guide to all possible spec file and rpmbuild options. For those you are invited to look at the latest snapshot of Maximum RPM .

Intended Audience

This guide is written for people who want to build packages in Fedora, either for themselves or for others.

Structure of a Package

  • Header

This contains things such as the name, version, release, epoch, and architecture the package was built for, as well as what this package requires from other packages and what it provides to other packages.

  • Files

This contains all files, directories, symlinks, etc. contained in the package, as well as the size, permissions, ownership, and SELinux file context.

  • Scripts

This contains the scripts to be run during package install and uninstall, as well as the script run during an RPM verify.

Creating a New Package

Tarballs are convenient. They can hold almost anything and they can be opened on almost any operating system. The problem is that files installed from a tarball are almost impossible to track. For this reason it's useful to be able to turn a tarball into a package. This chapter shows you examples of this.

Creating a non-root Buildroot

Most software source code comes in the form of tarballs (a tar archive compressed with either gzip or bzip2). It's possible to inadvertently have code in a makefile or script in a tarball that could damage your system. For this reason it's best to build packages as a user that doesn't have full access to the system.

Creating a non-root buildroot is easy:

  1. As root, install the rpmdevtools package: dnf install rpmdevtools
  2. As a normal user, run rpmdev-setuptree. This creates a ~/rpmbuild directory where packages are built as well as adding vital options to the ~/.rpmmacros file.
  3. Open ~/.rpmmacros in a text editor and add a few more useful options at the end of the file:
    • "%packager Your Name Here <Your E-mail Address>"
      • This allows other people to find you should they have questions if your package ever gets released into the public.
    • "%vendor Your Callsign Here"
      • This provides an easy way to separate your packages from packages of the same software made by other sources.

Case Study: leafpad

Leafpad is a simple application. For installation, it only requires the binary, the icon, the menu entry, and gettext translations. This makes it a good project to examine for packaging. The homepage for leafpad itself is at http://tarot.freeshell.org/leafpad.

The first step is to go into the buildroot and create a new spec file for your package. In a terminal run the following commands:

cd ~/rpmbuild/SPECS
rpmdev-newspec leafpad

This creates a new spec file called leafpad.spec.

rpmdev-newspec uses the package name to automatically select a spec template. The template is used when creating the new spec file. You can also specify a template using the -t option. Templates are stored in /etc/rpmdevtools.

Open the spec file in a text editor.

Name:           leafpad
Version:
Release:        1
Summary:

Group:
License:
URL:
Source0:
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

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

Since this is an unofficial package, the first thing you do is set the release to something lower than 1. Also, put a "tag" in the release to help show where the package comes from. Then follow it with the actual release in the sequence of packages that you may eventually release.

Next you fill in the version (as of writing this, 0.8.3) as well as add a summary pulled from the website.

Version:        0.8.3
Release:        0.docs.1
Summary:        A GTK+ based simple text editor

Now you need to categorize the package into an appropriate group. The groups are listed in /usr/share/doc/rpm-*/GROUPS or in RPMGroups . In this case the appropriate group is Applications/Editors since leafpad is a text editor.

For the license field, leafpad uses the GPL. From the leafpad website, get the URL for downloading leafpad 0.8.3.

Use this information to fill out the next section:

Group:          Applications/Editors
License:        GPL
URL:            http://tarot.freeshell.org/leafpad/
Source0:        http://savannah.nongnu.org/download/leafpad/leafpad-0.8.3.tar.gz

Next you must deal with BuildRequires and Requires. According to the leafpad webpage, it needs GTK+ >= 2.0.0 in order to build or run. You can let RPM figure out what libraries it needs to run, so fill in BuildRequires and comment out Requires for now.

BuildRequires:  gtk2-devel >= 2.0.0
#Requires:

Add a description for the package. The lines of text in %description should be at most 79 charaters long.

%description
Leafpad is a GTK+ based simple text editor. The user interface is similar to
Notepad. It aims to be lighter than GEdit and KWrite, and to be as useful as
them.

For now we'll skip most of the sections and just worry about %changelog at this time. Fill it in with the date (the command LC_TIME=en_US date +"%a %b %e %Y" will give you the exact text you should put), your name, your e-mail address, the version and release of the package, and a short description of what's changed in the package. Since this is the first package we'll just say that instead.

%changelog
* Gud Fez 74 6395 Foobly Barowitz <foobly@barowitz.tld> 0.8.3-0.docs.1
- Initial RPM release

So now the spec file should look like this (except with today's date and your name and e-mail address in %changelog):

Name:           leafpad
Version:        0.8.3
Release:        0.docs.1
Summary:        A GTK+ based simple text editor

Group:          Applications/Editors
License:        GPL
URL:            http://tarot.freeshell.org/leafpad/
Source0:        http://savannah.nongnu.org/download/leafpad/leafpad-0.8.3.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires:  gtk2-devel >= 2.0.0
#Requires:

%description
Leafpad is a GTK+ based simple text editor. The user interface is similar to
Notepad. It aims to be lighter than GEdit and KWrite, and to be as useful as
them.

%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
* Gud Fez 74 6395 Foobly Barowitz <foobly@barowitz.tld> 0.8.3-0.docs.1
- Initial RPM release

Now you need to actually download the source for leafpad and put it in the right place. Go ahead and download the tarball with your browser, wget, curl, or some other tool, then move it to ~/rpmbuild/SOURCES.

So now that you have the spec file and the sources, you need to actually go through the motions of building the package, which involves going through to the end of %build. Type the following commands in a terminal:

cd ~/rpmbuild/SPECS
rpmbuild -bc leafpad.spec

At this point you will most likely end up with an error message, especially if you've never built any software on your machine before. Let's work through them one at a time.

  • bash: /usr/bin/rpmbuild: No such file or directory

This indicates that you need to install the rpm-build package, however you should already have it installed since it's a dependency of rpmdevtools which you had to install in order to previously create the non-root buildroot.

  • error: Failed build dependencies:

gtk2-devel >= 2.0.0 is needed by leafpad-0.8.3-0.docs.1.i386

This message tells you that you need to install files or packages in order to build the package. You can use dnf to install the files or packages that it indicates. In this case you would use dnf install gtk2-devel in order to do so.

Once you get a clean build the last line of output will be + exit 0. You can then go ahead and test %install like so:

rpmbuild -bi leafpad.spec

At this point you definitely will see errors:

RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/bin/leafpad
/usr/share/applications/leafpad.desktop
/usr/share/locale/bg/LC_MESSAGES/leafpad.mo
/usr/share/locale/ca/LC_MESSAGES/leafpad.mo
/usr/share/locale/cs/LC_MESSAGES/leafpad.mo
/usr/share/locale/de/LC_MESSAGES/leafpad.mo
/usr/share/locale/es/LC_MESSAGES/leafpad.mo
/usr/share/locale/fr/LC_MESSAGES/leafpad.mo
/usr/share/locale/hu/LC_MESSAGES/leafpad.mo
/usr/share/locale/it/LC_MESSAGES/leafpad.mo
/usr/share/locale/ja/LC_MESSAGES/leafpad.mo
/usr/share/locale/lt/LC_MESSAGES/leafpad.mo
/usr/share/locale/pl/LC_MESSAGES/leafpad.mo
/usr/share/locale/pt/LC_MESSAGES/leafpad.mo
/usr/share/locale/ru/LC_MESSAGES/leafpad.mo
/usr/share/locale/sk/LC_MESSAGES/leafpad.mo
/usr/share/locale/sv/LC_MESSAGES/leafpad.mo
/usr/share/locale/ta/LC_MESSAGES/leafpad.mo
/usr/share/locale/vi/LC_MESSAGES/leafpad.mo
/usr/share/locale/zh_CN/LC_MESSAGES/leafpad.mo
/usr/share/locale/zh_TW/LC_MESSAGES/leafpad.mo
/usr/share/pixmaps/leafpad.png

The next step is to take all those files and put them in %files. But just copying and pasting them would be the wrong thing to do.

RPM has a number of macros that can be used to shorten the spec file or add needed flexibility. You can see some of the directory-based macros in RPMMacros . You can get a complete list by running rpm --showrc.

So go ahead and add all of the files except the ones under /usr/share/locale to %files, substituting macros where appropriate.

%files
%defattr(-,root,root,-)
%doc
%{_bindir}/leafpad
%{_datadir}/applications/leafpad.desktop
%{_datadir}/pixmaps/leafpad.png

Now you just need to handle the files under /usr/share/locale. Fortunately Fedora provides the %find_lang macro for that. It looks for all files under /usr/share/locale that match the argument you pass it and puts them in a file of the same name appended with ".lang". The macro is called at the end of %install. Since the name of the package is "leafpad" we can just use the appropriate macro instead.

%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%find_lang %{name}

And you also need to tell %files to include the files listed in the generated file.

%files -f %{name}.lang
%defattr(-,root,root,-)
%doc
%{_bindir}/leafpad
%{_datadir}/applications/leafpad.desktop
%{_datadir}/pixmaps/leafpad.png

At this point the package will build, but there is another step to take before you're ready.

You need to fill in the %doc specifier in %files with any documentation for the app. Go to ~/rpmbuild/BUILD/leafpad-0.8.3 and look at the files there. Most of them are only needed to build the app, but some are useful to include. The specific files you're interested in are AUTHORS, ChangeLog, COPYING, NEWS, and README, so go ahead and add them to %doc in %files.

%files -f %{name}.lang
%defattr(-,root,root,-)
%doc AUTHORS ChangeLog COPYING NEWS README
%{_bindir}/leafpad
%{_datadir}/applications/leafpad.desktop
%{_datadir}/pixmaps/leafpad.png

Now you can go ahead and test the package again.

rpmbuild -bi leafpad.spec

Success! You can now go ahead and build the binary and source packages.

rpmbuild -ba leafpad.spec

Close to the end of the process you'll see three files being written. The first one is the source package, the second is the binary package, and the third is the debug information for the binary package. If you already have leafpad installed from Fedora go ahead and remove it now, and install the new binary package as root.

rpm -e leafpad
rpm -Uvh /path/to/leafpad-0.8.3-0.docs.1.$(uname -i).rpm

After installing it a menu entry named Leafpad should appear under Accessories. Go ahead and run it, and try it out a bit.

You may notice that leafpad currently uses the old-style GTK+ file dialog box for opening and saving files. In order to make it use the new-style file dialog box you need to pass an option to configure. Normally you would run ./configure --help to show us the different options, but the act of producing a proper package wipes the necessary files. In a terminal run the following:

cd ~/rpmbuild/SPECS
rpmbuild -bp leafpad.spec

This goes through just the %prep section of the spec file (unpacks the tarball and applies any patches). Now you can do the following to see the options:

cd ~/rpmbuild/BUILD/leafpad-0.8.3
./configure --help

In this case the option you're looking for is --enable-chooser. Go ahead and modify %build appropriately.

%build
%configure --enable-chooser
make %{?_smp_mflags}

Now you need to increment the release and add the %changelog entry.

Version:        0.8.3
Release:        0.docs.2
Summary:        A GTK+ based simple text editor
%changelog
* Wed Jan 30 2008 Foobly Barowitz <foobly@barowitz.tld> 0.8.3-0.docs.2
- Enabled new-style GTK+ file dialog

* Mon Jan 21 2008 Foobly Barowitz <foobly@barowitz.tld> 0.8.3-0.docs.1
- Initial RPM release

So now rebuild the package.

rpmbuild -ba leafpad.spec

Since the release of the new package is higher than the old package you can just upgrade it directly as root.

rpm -Uvh /path/to/leafpad-0.8.3-0.docs.2.$(uname -i).rpm

When you run it you will see that loading and saving files now uses the new-style file dialog box.

rpmbuild -ba builds the binary and source packages. You can also build just the binary package (with -bb), or just the source package (with -bs).

Case Study: OpenEXR

OpenEXR is a library created by Industrial Light & Magic for creating and using high dynamic range images. Create and fill in a spec file for OpenEXR in the same manner as you did for leafpad.

Name:           OpenEXR
Version:        1.2.2
Release:        0.docs.1
Summary:        A high dynamic-range (HDR) image file format

Group:          System Environment/Libraries
License:        BSD
URL:            http://www.openexr.com/
Source0:        http://savannah.nongnu.org/download/openexr/OpenEXR-1.2.2.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires:
#Requires:

%description
OpenEXR is a high dynamic-range (HDR) image file format developed by
Industrial Light & Magic for use in computer imaging applications.

%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
* Gud Fez 74 6395 Foobly Barowitz <foobly@barowitz.tld> 1.2.2-0.docs.1
- Initial RPM release

Unfortunately the OpenEXR website is far less forthcoming regarding what packages are required to build the library. Comment out the BuildRequires line for now, download the source tarball, and run the prep stage.

#BuildRequires:
#Requires:
rpmbuild -bp OpenEXR.spec

Go into the BUILD/OpenEXR-1.2.2 directory and open the INSTALL file there. Most packages have build instructions in this file. Some even include a list of build requirements. Unfortunately OpenEXR does not.So then close this file and open README instead. About halfway through the file it reads "exrdisplay requires FLTK 1.1 or greater" so go ahead and add this to BuildRequires, remembering to uncomment it.

BuildRequires:  fltk-devel >= 1.1
#Requires:

Now try to compile the package:

rpmbuild -bc OpenEXR.spec
error: Failed build dependencies:
fltk-devel >= 1.1 is needed by OpenEXR-1.2.2-0.docs.1.i386

Of course, you need to actually install the fltk-devel package in order to build OpenEXR, so do so now with dnf. Once that is done, try compiling the package again.

checking for strerror... yes
checking for compress in -lz... no
configure: error:
*** OpenEXR requires a recent version of zlib, which you don't appear to
*** have.
***
*** This could be because the run-time linker is not finding zlib, or it
*** is finding the wrong version.  In this case, you'll need to set your
*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point
*** to the proper version.  Also, make sure you have run ldconfig if
*** that is required on your system.

error: Bad exit status from /var/tmp/rpm-tmp.XXXXX (%build)


RPM build errors:
Bad exit status from /var/tmp/rpm-tmp.XXXXX (%build)

It seems that FLTK isn't the only package required to build OpenEXR. Go ahead and add zlib-devel to BuildRequires and install it via dnf.

BuildRequires:  fltk-devel >= 1.1 zlib-devel
#Requires:

Now compile once more.

[more messages above] 
makeTiled.cpp: In function 'void<unnamed>::reduceY(const TypedImageChannel<T>&, TypedImageChannel<T>&, bool, Extrapolation, bool) [with T = unsigned int] ':
makeTiled.cpp:447:   instantiated from here
makeTiled.cpp:318: error: 'class TypedImageChannel<unsigned int>' has no member named 'image'
makeTiled.cpp:319: error: 'const class TypedImageChannel<unsigned int>' has no member named 'image'
makeTiled.cpp:320: error: 'class TypedImageChannel<unsigned int>' has no member named 'image'
make[1] : *** [makeTiled.o]  Error 1
make: *** [all-recursive]  Error 1
error: Bad exit status from /var/tmp/rpm-tmp.XXXXX (%build)


RPM build errors:
Bad exit status from /var/tmp/rpm-tmp.XXXXX (%build)

Isn't this an interesting predicament. What has happened here is that the compiler has detected a problem in the source and is failing because the compiler cannot make sense of it. If you scroll back to the first few error messages you can get a clue about what is causing the error.

./Image.h:59: error: expected <code>)' before '&' token
./Image.h:64: error: ISO C++ forbids declaration of 'Image' with no type
./Image.h:64: error: expected ';' before '&' token

In this case the second line is saying that the source is using a datatype before it knows that the datatype exists. From the line we see that the problem is in a file called Image.h. Scroll up a bit further to find out where this file is located.

Making all in exrmaketiled
make[1] : Entering directory <code>~/rpmbuild/BUILD/OpenEXR-1.2.2/exrmaketiled'

Go to this directory, open Image.h, and go to line 59.

class ImageChannel
{
public:

friend class Image;

ImageChannel (Image &Image);
virtual ~ImageChannel();

virtual Imf::Slice  slice () const = 0;

Image &             image ()                {return _image;}
const Image &       image () const          {return _image;}

What you have here is the beginning of the declaration for the ImageChannel class. When you examine the problems in the error messages listed above in relation to the source code you will notice that they all refer to Image. The C++ standard dictates that you cannot use a type until the compiler knows about it, however the friend declaration on line 57 is not enough to do so. You must add a line telling the compiler about the Image class. But before you do so, copy the file to Image.h.friend:

cp Image.h Image.h.friend

Now put a forward declaration of the Image class before the declaration of the ImageChannel class. You are supposed to edit the original file, not the new Image.h.friend.

class Image;

class ImageChannel
{

Since RPM uses pristine tarballs to build packages, you now need to make a patch containing the difference between the original file and the modified file. RPM provides a convenient tool for this called gendiff. When run against a directory and passed a suffix it will compare all files with that suffix to the corresponding files without that suffix and generate a patch with the changes:

cd ~/rpmbuild/BUILD
gendiff OpenEXR-1.2.2 .friend > ../SOURCES/OpenEXR-1.2.2-friend.patch

The generated patch should look similar to this:

--- OpenEXR-1.2.2/exrmaketiled/Image.h.friend   YYYY-MM-DD hh:mm:ss.xxxxxxxxx -ZZZZ
+++ OpenEXR-1.2.2/exrmaketiled/Image.h  YYYY-MM-DD hh:mm:ss.xxxxxxxxx -ZZZZ
@@ -49,6 +49,7 @@

+class Image;

class ImageChannel
{

Now add the patch to the spec file so that rpmbuild uses it during the build.

Source0:        http://savannah.nongnu.org/download/openexr/OpenEXR-1.2.2.tar.gz
Patch0:         OpenEXR-1.2.2-friend.patch
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%prep
%setup -q
%patch0 -p1 -b .friend

%build

Compile the package once again.

make[1] : Leaving directory <code>/home/ignacio/work/rpmbuild/BUILD/OpenEXR-1.2.2/exrenvmap'
make[1] : Entering directory <code>/home/ignacio/work/rpmbuild/BUILD/OpenEXR-1.2.2'
make[1] : Nothing to be done for <code>all-am'.
make[1] : Leaving directory <code>/home/ignacio/work/rpmbuild/BUILD/OpenEXR-1.2.2'
+ exit 0

Much better. Now initiate the install phase:

rpmbuild -bi OpenEXR.spec
RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/bin/exrdisplay
/usr/bin/exrenvmap
/usr/bin/exrheader
/usr/bin/exrmakepreview
/usr/bin/exrmaketiled
/usr/bin/exrstdattr
/usr/include/OpenEXR/Iex.h
/usr/include/OpenEXR/IexBaseExc.h
...
/usr/include/OpenEXR/halfFunction.h
/usr/include/OpenEXR/halfLimits.h
/usr/lib/libHalf.a
/usr/lib/libHalf.la
/usr/lib/libHalf.so
/usr/lib/libHalf.so.2
/usr/lib/libHalf.so.2.0.2
/usr/lib/libIex.a
/usr/lib/libIex.la
/usr/lib/libIex.so
/usr/lib/libIex.so.2
/usr/lib/libIex.so.2.0.2
/usr/lib/libIlmImf.a
/usr/lib/libIlmImf.la
/usr/lib/libIlmImf.so
/usr/lib/libIlmImf.so.2
/usr/lib/libIlmImf.so.2.0.2
/usr/lib/libImath.a
/usr/lib/libImath.la
/usr/lib/libImath.so
/usr/lib/libImath.so.2
/usr/lib/libImath.so.2.0.2
/usr/lib/pkgconfig/OpenEXR.pc
/usr/share/aclocal/openexr.m4
/usr/share/doc/OpenEXR-1.2.2/examples/drawImage.cpp
/usr/share/doc/OpenEXR-1.2.2/examples/drawImage.h
...
/usr/share/doc/OpenEXR-1.2.2/examples/rgbaInterfaceTiledExamples.cpp
/usr/share/doc/OpenEXR-1.2.2/examples/rgbaInterfaceTiledExamples.h

That's quite a large number of files. Fortunately you don't need to specify every single file; specifying a directory will include every file and directory under it. But before you add them, notice that many of the files are libraries and C/C++ headers. You will need to create a devel subpackage to contain the files required for building packages against OpenEXR.

%description
OpenEXR is a high dynamic-range (HDR) image file format developed by
Industrial Light & Magic for use in computer imaging applications.

%package        devel
Summary:        Headers and libraries for building apps that use OpenEXR
Group:          Development/Libraries
Requires:       %{name} = %{version}-%{release}

%description    devel
This package contains headers and libraries required to build applications that
use the OpenEXR format.

%prep
%files
%defattr(-,root,root,-)
%doc

%files devel
%defattr(-,root,root,-)
%doc

%changelog

The tarball also rudely places examples in the documentation directory of the main package when it would make more sense to have them in the devel subpackage.

%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
rm -rf $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}-%{version}/examples

All executables and *.so.* files should be placed in the main package.

%files
%defattr(-,root,root,-)
%doc
%{_bindir}/*
%{_libdir}/*.so.*

All headers, static libraries, libtool archives, *.so files, autotools, and pkgconfig files go in the -devel subpackage.

%files devel
%defattr(-,root,root,-)
%doc
%{_includedir}/%{name}
%{_libdir}/*.a
%{_libdir}/*.la
%{_libdir}/*.so
%{_libdir}/pkgconfig/%{name}.pc
%{_datadir}/aclocal/*.m4

We should add the examples to the documentation in the -devel subpackage. Normally we would use

%doc examples

to do this, but unfortunately there are 2 problems. The first is that the directory isn't called "examples". By looking at the various files under BUILD/OpenEXR-1.2.2 we find that the directory is actually called "IlmImfExamples". The second problem is that the directory will contain several files that we don't want included in the final package. What should be done is to copy it in %prep to the proper name and remove to undesired files, and then add it to %doc in -devel, and any appropriate files to %doc in the main package.

%prep
%setup -q
%patch0 -p1 -b .friend
cp -a IlmImfExamples examples
rm examples/Makefile*
%files
%defattr(-,root,root,-)
%doc AUTHORS ChangeLog LICENSE NEWS README
%{_bindir}/*
%{_libdir}/*.so.*

%files devel
%defattr(-,root,root,-)
%doc examples
%{_includedir}/%{name}
%{_libdir}/*.a
%{_libdir}/*.la
%{_libdir}/*.so
%{_libdir}/pkgconfig/%{name}.pc
%{_datadir}/aclocal/*.m4

Since the package places shared libraries into normal directories /sbin/ldconfig will have to be invoked after the package has been installed or removed as described in ScriptletSnippets.

%clean
rm -rf $RPM_BUILD_ROOT

%post -p /sbin/ldconfig

%postun -p /sbin/ldconfig

%files

So now if you rebuild it you will find that it builds cleanly. Go ahead and build and then install the binary packages.

rpmbuild -bb OpenEXR.spec

rpm -Uvh /path/to/OpenEXR-1.2.2-0.docs.1.arch.rpm /path/to/OpenEXR-devel-1.2.2-0.docs.1.arch.rpm

Appendix: mock: Chroot Buildtool

Turning a spec file, a tarball, and patches into an actual package is a complex process. Installed development packages can poison the package with unexpected capabilities, which will cause users of the package to complain when their builds lack those capabilities. Additionally, it is very easy to miss BuildRequires that need to be added to the spec file.

Unix-class operating systems provide a facility known as chroot which causes a subdirectory to become the new root directory temporarily. mock is a tool which takes advantage of this facility to provide a "cleanroom" build of a source package. Only a basic set of packages plus the packages listed in BuildRequires are installed into the chroot and then mock builds the package under the chroot. This creates a clean, reproducible build of the package.

mock takes the following command line arguments:

  • -r <root>: This gives the root you want to use. It should match one of the configuration files in /etc/mock.
  • --resultdir=<directory>: This tells mock where you want the logs and built packages to be placed.
  • <source package>: This is the source RPM you want to rebuild.

E.g., mock -r fedora-4-i386-core --resultdir=mock-leafpad-0.8.3 leafpad-0.8.3-0.docs.2.src.rpm

The configuration file is actually a Python script that creates a dictionary (config_opts) that contains all the values used by mock for generating the chroot and building source packages. The keys in the dictionary include:

  • root: The directory under /var/lib/mock in which to generate the chroot.
  • target_arch: The processor architecture the chroot handles.
  • yum.conf: The yum configuration file content used by the chroot.
  • macros: A set of RPM macros to be initialized by mock.