A Beginner's Guide to Creating RPM Packages Using RPM Spec Files

A Beginner's Guide to Creating RPM Packages Using RPM Spec Files

Introduction

RPM (Red Hat Package Manager) is a powerful tool for building, installing, and managing software packages on Linux systems. Understanding how to create RPMs using spec files is an essential skill for any DevOps engineer or Linux administrator. This guide will take you through the steps of writing an RPM spec file, creating an RPM package, and installing it. We'll include practical demos with increasing complexity to reinforce your learning.


What is an RPM Spec File?

A spec file is the heart of an RPM package. It contains all the information needed to build the package, including:

  • Metadata (e.g., name, version, release, summary)

  • Source files and patches

  • Build instructions

  • File installation paths

  • Scripts (pre-install, post-install, etc.)


Structure of a Spec File

An RPM spec file is divided into sections:

  1. Header Section: Defines package metadata such as name, version, and release.

  2. Description Section: Provides details about the package.

  3. Preparation Section: Prepares the source code for building.

  4. Build Section: Contains the commands to compile the software.

  5. Install Section: Defines the commands to install files.

  6. File Section: Lists the files to include in the package.

  7. Scriptlets: Contains scripts for pre-installation, post-installation, etc.


Step-by-Step Guide

1. Setting Up the Environment

Ensure you have the necessary tools installed:

sudo  dnf -y install rpmdevtools rpmlint

Create the RPM build environment:

rpmdev-setuptree # this command will setup the necessary folder structure to work with rpm

Add the following to your ~/.rpmmacros file: This file will be used to pick up any macros used in .spec file.

%_topdir %(echo $HOME)/rpmbuild
%packager DurgaPrasadAnanta <your-gmail-here@gmail.com>

2. Writing the Spec File

Create a file named example.spec under ~/rpmbuild/SPECS/.

Spec File Example
Name:           myapp
Version:        1.0.0
Release:        1%{?dist}
Summary:        A simple demo application

License:        MIT
URL:            https://example.com/myapp
Source0:        myapp-1.0.0.tar.gz

BuildRequires:  gcc make
Requires:       glibc >= 2.17

%description
MyApp is a simple application demonstrating RPM packaging.

%prep
%setup -q

%build
make

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

%files
%license LICENSE
%doc README.md
/usr/local/bin/myapp
/usr/local/share/myapp/*

%changelog
* Fri Jan 10 2025 Your Name <your.email@example.com> - 1.0.0-1
- Initial RPM release

3. Preparing the Source Code

Place your source code in ~/rpmbuild/SOURCES/:

tar -czvf ~/rpmbuild/SOURCES/myapp-1.0.0.tar.gz myapp/

Ensure the directory structure matches what you’ve described in the %install section.

4. Building the RPM

Run the build command:

rpmbuild -ba ~/rpmbuild/SPECS/example.spec

Check the ~/rpmbuild/RPMS/ directory for the generated RPM.

5. Installing and Verifying the RPM

Install the RPM:

sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/myapp-1.0.0-1.x86_64.rpm

Verify the installation:

rpm -ql myapp

Practical Demo 1: Packaging a Simple C Application

For this demo, we will package a simple C application. The application prints "Hello, World!" to the console.

Preparing the Application

Create a directory for the source code:

mkdir -p ~/myapp
cd ~/myapp

Add the following files:

main.c:

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

Makefile:

CC=gcc
CFLAGS=-Wall

all: myapp

myapp: main.o
    $(CC) -o myapp main.o

main.o: main.c
    $(CC) $(CFLAGS) -c main.c

install:
    mkdir -p $(DESTDIR)/usr/local/bin
    cp myapp $(DESTDIR)/usr/local/bin/

clean:
    rm -f *.o myapp

LICENSE and README.md: Include these files for completeness.

Packaging the Application

Compress the source code:

tar -czvf ~/rpmbuild/SOURCES/myapp-1.0.0.tar.gz .

Create the spec file (example.spec) as shown earlier. Build the RPM and install it.


Useful commands

rpm -qa
# This command returns every package installed on your system, quite a few packages.
#The packages are returned one per line, as shown following.

rpm -qi package
#The –i option with an rpm query command tells the rpm command to output descriptive information about the package. 
#You can also use the longer option, --info, in place of –i. The basic syntax is:


rpm –ql package
#The –l (ell) option queries all the files in a package. 
#You can also use the longer option, --list, in place of –l. 

rpm2cpio /home/ec2-user/rpmbuild/SRPMS/myapp-1.0.0-1.amzn2023.src.rpm  | cpio -idmv
# This unarchives the source rpm and outputs spec file, source code tarball, patches etc..

Tips and Best Practices

  1. Validation: Use rpmlint to validate your spec files and RPMs.
rpmlint ~/rpmbuild/SRPMS/*.src.rpm ~/rpmbuild/RPMS/x86_64/*.rpm
  1. Macros: Leverage macros like %{_bindir} and %{_libdir} for consistency.

  2. Dependencies: Clearly define BuildRequires and Requires to avoid runtime issues.

  3. Changelog: Maintain an accurate changelog for version tracking.


Useful Resources

https://rpm-packaging-guide.github.io/#introduction

https://rpm-software-management.github.io/rpm/manual/spec.html

https://jfearn.fedorapeople.org/en-US/RPM/4/html/RPM_Guide/ch-using-rpm-db.html

Conclusion

Creating RPMs using spec files might seem daunting initially, but with practice, it becomes straightforward. By following this guide, you now have a strong foundation to package and distribute software effectively. Explore additional features like patches, advanced scriptlets, and dependency management to enhance your packaging skills.