Back to Basics: Creating an RPM

Posted: August 27, 2013 in Software
Tags:

This upcoming Saturday, I will be at the Fredericksburg Linux Users Group (fredlug.info) presenting on the topic of Creating RPMs.  While not as flashy as the latest buzz in Cloud, application development or virtualization, it is a skill that can be used to package files in an easy to deploy format.  During the presentation, I will be covering what an RPM is and how to make a very simple RPM that contains exactly one executable file – kind of a spin on the ever popular “Hello World” test app.  I thought it would also be convenient to post the information that I am presenting here so that others can access it as well.

Components of an RPM File

An RPM file contains three components; metadata, files and scripts and follow a common naming convention of:
nameversionrelease.architecture.rpm
The elements of name, version, release and architecture are all included as elements of the metadata component of the RPM package.  Metadata is simply data about the package.  Other parts of the metadata include the name of the builder, the date the RPM was built, and dependencies that must be satisfied to install the RPM.

The second component of an RPM is the files that will be written to disk when the RPM is installed.

The final component is the scripts that are included in the package.  These scripts control actions that take place on a system when the package is installed, uninstalled or updated and can include things such as restarting a service or creating supporting user and group accounts.

The “.spec” File

At the heart of RPM building is the .spec file.  This file contains a preamble section, which contains the metadata about the package, and other sections that provide information on how the RPM file should be built,  how the files contained in the manifest  are written to the disk when the package is installed, and a changelog where changes that are made to an RPM can be recorded.

The fields in the preamble include:

  • Name – the name of the package
  • Version – the version of the package
  • Release – release number as chosen by the builder
  • Group – the group the the package belongs to (/usr/share/doc/rpm-*/GROUPS)
  • URL – the website of the open source software
  • License – Short License Identifier (APL 1.0, GPLv3, etc)
  • Summary – one line description about the package
  • Source – the file to be used as source code
  • BuildArch – the architecture to use when building the package
  • Requires – explicit requirements the package relies on
  • BuildRequires – list of requirements to build the package

There are many other fields in the .spec file as well.  The names of these other sections are:

  • %description – a long description of what the software contained in the RPM is or does.
  • %prep – used to provide instructions to uncompress and unarchive the files into the BUILD directory
  • %build – instructions to “build” (aka compile) the software if needed
  • %install – shows the locations that files should be installed to
  • %clean – post build cleanup
  • %files – all files should be listed here and if they are configuration or documentation files, they should be listed in the %config or %doc section as appropriate.  This allows the configuration and documentation to be displayed when a user runs rpm -qd  or rpm -qc against the installed package.
  • %changelog – a log of changes made to the software which typically includes bug information

When making a .spec file it is important not to repeat any work that someone else may have done already.  There is a good chance that a .spec file has already been created by someone else for the package that you are creating.  If that is the case, you can retrieve the source rpm (denoted by src.rpm at the end of the package name) using yumdownloader, a web browser, an FTP program, etc.  Once it is downloaded it can be installed using the rpm utility or extracted with the rpm2cpio command (rpm2cpio package-name-*.src.rpm | cpio -id).

For this article, we are going to create our own source and .spec files and will not explore .spec files that others have made (it is a basic article after all).

Preparing The Build Environment

The following instructions have been tested on Fedora 18 and 19 and include the use of applications from the rpmdevtools package which includes utilities that can make package building much easier.

To install the tools we need to build an RPM run the following command as root:

 yum install @development-tools fedora-packager

Next, create a non-root user that we can use to build RPM packages.  We do not want to risk affecting our system if some of the commands in the .spec file are inadvertently run, so using an account that is not root or or normal user account can minimize the effects of mistakes.  To create the “rpmmaker” account, run the following as root.

 # useradd rpmmaker
 # usermod -a -G mock rpmmaker
 # passwd rpmmaker

Note: I add the rpmmaker user to the mock group but it is not necessary.  Mock is a system for  building RPM packages in chroot environments that it sets up based on a configuration file that it is fed.  The Fedora Project uses it to build their “Koji” packages.  Someday I hope to explore it a bit more.

Once the user has been created you can either log out and log back in as that user, or you can run “su – rpmmaker” to become that user and use its shell.  Next, run the following command to configure the build environment.

 $ rpmdev-setuptree

To sign the RPMs so that their integrity can be checked, we have to run several commands, starting with creating a GPG keyring and creating a key.  There are a few prompts that you have to provide input for, just follow the below for a good example.

 $ cd ~
 $ mkdir .gnupg
 $ gpg --gen-key
 gpg (GnuPG) 1.4.14; Copyright (C) 2013 Free Software Foundation, Inc.
 This is free software: you are free to change and redistribute it.
 There is NO WARRANTY, to the extent permitted by law.
 gpg: keyring `/home/rpmmaker/.gnupg/secring.gpg' created
 gpg: keyring `/home/rpmmaker/.gnupg/pubring.gpg' created
 Please select what kind of key you want:
 (1) RSA and RSA (default)
 (2) DSA and Elgamal
 (3) DSA (sign only)
 (4) RSA (sign only)
 Your selection? 1
 RSA keys may be between 1024 and 4096 bits long.
 What keysize do you want? (2048) 2048
 Requested keysize is 2048 bits
 Please specify how long the key should be valid.
 0 = key does not expire
 n = key expires in n days
 w = key expires in n weeks
 m = key expires in n months
 y = key expires in n years
 Key is valid for? (0)
 Key does not expire at all
 Is this correct? (y/N) y
 You need a user ID to identify your key; the software constructs the user ID
 from the Real Name, Comment and Email Address in this form:
 "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
 Real name: Ted Brunell
 Email address: myemail@address.com
 Comment: RPM Signing key
 You selected this USER-ID:
 "Ted Brunell (RPM Signing key) <myemail@address.com>"
 Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
 You need a Passphrase to protect your secret key.
 We need to generate a lot of random bytes. It is a good idea to perform
 some other action (type on the keyboard, move the mouse, utilize the
 disks) during the prime generation; this gives the random number
 generator a better chance to gain enough entropy.
 .+++++
 .....+++++
 We need to generate a lot of random bytes. It is a good idea to perform
 some other action (type on the keyboard, move the mouse, utilize the
 disks) during the prime generation; this gives the random number
 generator a better chance to gain enough entropy.
 ...+++++
 +++++
 gpg: /home/rpmmaker/.gnupg/trustdb.gpg: trustdb created
 gpg: key 2417CF67 marked as ultimately trusted
 public and secret key created and signed.
 gpg: checking the trustdb
 gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
 gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
 pub 2048R/2417CF67 2013-08-27
 Key fingerprint = 5734 EF65 320F A538 EE23 44C2 4723 6BED 2417 CF67
 uid Ted Brunell (RPM Signing key) <myemail@address.com>
 sub 2048R/F7657C83 2013-08-27

After the key is created, we need to import the key into the RPM database.

 $ gpg --list-keys
 /home/rpmmaker/.gnupg/pubring.gpg
 ----------------------------
 pub 2048R/2417CF67 2013-08-27
 uid Ted Brunell (RPM Signing key) <myemail@address.com>
 sub 2048R/F7657C83 2013-08-27
 $ gpg --export -a 'Ted Brunell' > RPM-GPG-KEY-tedbrunell
 $ su -
 # rpm --import /home/rpmmaker/RPM-GPG-KEY-tedbrunell
 # rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n'
 gpg-pubkey-de7f38bd-501f4964 --> gpg(Fedora (18) <fedora@fedoraproject.org>)
 gpg-pubkey-7fac5991-4615767f --> gpg(Google, Inc. Linux Package Signing Key <linux-packages-keymaster@google.com>)
 gpg-pubkey-e31b30ca-4f3428cf --> gpg(RPM Fusion nonfree repository for Fedora (18) <rpmfusion-buildsys@lists.rpmfusion.org>)
 gpg-pubkey-982e0a7c-4f34288f --> gpg(RPM Fusion free repository for Fedora (18) <rpmfusion-buildsys@lists.rpmfusion.org>)
 gpg-pubkey-fb4b18e6-50b96bfd --> gpg(Fedora (19) <fedora@fedoraproject.org>)
 gpg-pubkey-172ff33d-503292b0 --> gpg(RPM Fusion free repository for Fedora (19) <rpmfusion-buildsys@lists.rpmfusion.org>)
 gpg-pubkey-f6777c67-45e5b1b9 --> gpg(Adobe Systems Incorporated (Linux RPM Signing Key) <secure@adobe.com>)
 gpg-pubkey-cd30c86b-503293b9 --> gpg(RPM Fusion nonfree repository for Fedora (19) <rpmfusion-buildsys@lists.rpmfusion.org>)
 gpg-pubkey-2417cf67-521c1301 --> gpg(Ted Brunell (RPM Signing key) <myemail@address.com>)

If your key is listed, you are in good shape.  If not, try again.  The next step is to add a couple of lines to the ~/.rpmmacros file.

 %_signature gpg
 %_gpg_name Ted Brunell

Create Our Source File

As I mentioned before, we are going to create our own source file and populate it with a single executable script.  To create that file, enter the following commands as the rpmmaker user.

 $ mkdir test-1.0
 $ cat << EOF > test-1.0/myscript.sh
 #!/bin/bash
 echo "Hello, the date and time is: `date`"
 EOF
 $ tar czvf test-1.0-1.tar.gz test-1.0
 test-1.0/
 test-1.0/myscript.sh

Notice, I added a “-1” to the name of the test-1.0 directory when the tar.gz file was created.  The -1 signifies the release version of the package.  After the file has been created, copy the file into the SOURCES directory in the build environment that was previously set up.

 $ cp test-1.0-1.tar.gz ~/rpmbuild/SOURCES/

Configure the .spec File

The next thing that needs to be done is to create the .spec file that was discussed earlier.  The steps to accomplish this are:

 $ cd ~/rpmbuild/SPECS
 $ rpmdev-newspec test
 test.spec created; type minimal, rpm version >= 4.11.

Edit the file so that the contents are similar to the below and save it.

 $ vi test.spec
 Name: test
 Version: 1.0
 Release: 1
 Summary: Test Program
 License: GPL
 URL: http://myopensourcelife.com
 Source0: %{name}-%{version}-%{release}.tar.gz
 BuildRoot: /tmp/%{name}-buildroot
 BuildArch: noarch
 BuildRequires: /bin/rm, /bin/mkdir, /bin/cp
 Requires: /bin/bash, /bin/date
 %description
 A testing package that installs a single file
 %prep
 %setup -q
 %build
 #make %{?_smp_mflags}
 %install
 rm -rf $RPM_BUILD_ROOT
 mkdir -p $RPM_BUILD_ROOT/usr/local/bin
 cp myscript.sh $RPM_BUILD_ROOT/usr/local/bin
 %clean
 rm -rf $RPM_BUILD_ROOT
 %files
 %defattr(-,root,root,-)
 %attr(0755,root,root)/usr/local/bin/myscript.sh
 #%doc
 %changelog
 * Sat Aug 25 2013 Ted Brunell 1.0-1
 - Initial RPM

I removed the %configure macro line since it can cause conflicts if it is left in place when no files are present that it needs to take action on.  I also commented out the two “make” lines since there again is no reason to take any compiler actions on the source package we are using to build our package.  The %defattr macro sets the default attributes for all files in the manifest.  The %attr macro sets the file permissions and ownership.  Additionally I commented out the %config and %doc sections since there are no configuration or documentation files in the package.

Building and Testing the RPM

The source code is available, the .spec file is configured and the almost final step is to actually build the RPM package.  The syntax of the command we want to run is rpmbuild -ba .  The -ba option means to build both a source package (src.rpm) and the install-able package.  That option can be switched to -bb is all you want is the final install-able RPM package.  Optionally, the –sign setting to sign the RPM with the GPG key when it is being built.

 $ rpmbuild -ba --sign ~/rpmbuild/SPECS/test.spec

If everything goes well, there will not be any errors during the build process.  If there are errors, recheck your .spec file.

To test the RPM, the following commands will install, look for the single file we are trying deploy and then run it.  Run them as root.

 # yum localinstall \ /home/rpmmaker/rpmbuild/RPMS/noarch/test-1.0-1.noarch.rpm
 # ls -al /usr/local/bin/myscript.sh
 # /usr/local/bin/myscript.sh

If everything goes well, congratulations, you have successfully created, signed and deployed your first custom RPM package.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s