If you’ve been using Linux for some time now, you must have heard the term packages and names like RPM packages or DEB packages. You might have also installed some of these packages on your system. Packages are just like software.
In the way we have .exe files in Windows, in the Linux ecosystem we have packages to install.
In this tutorial, we’ll create a RedHat Linux package or RPM package of our own, which is intended for distros such as Fedora, CentOS, Rocky Linux, AlmaLinux, openSUSE, and others. We will write a simple hello world script and create an RPM package for it.
Table of Contents
What is a Linux Package?
A package is a compressed file or archive that contains all the files with a given application in Linux distributions. The files are normally stored in the package in the order in which they were installed on the system. Most packages additionally include instructions for installing as well as a list of dependencies.
The most popular file extensions of packages are
- .deb – used by all Debian type Linux
- .rpm – used by RedHat type Linux
- .tgz – mainly different TAR Packages (used by Arch Linux and Slackware)
So you can now understand these packages are named by their extensions. Packages can be installed via commands – Debian packages can be installed via the dpkg
command, RPM packages can be installed via the rpm
command.
Linux packages generally don’t provide the dependencies needed to install them. So different Linux distros use their own package managers for proper installation of the packages.
APT, YUM, Pacman are examples of such package managers. You can easily download, install, update, remove, and do all sorts of things with packages by using these package managers.
There are packages provided by companies, communities, corporations and other contributors from all over the world. Different software developers create packages of their own.
You may be thinking that it would be really amazing if you can create your own package. For example, you have written a mini software script and you want to make it available for everybody to install and use it. To do this, all you have to do is create a package from it and share this package with others to install on their systems.
What is an RPM package?
RPM is a shorthand for Red Hat Package Manager. It was created by Red Hat and is used on Linux operating systems that are based on Red Hat (Fedora, CentOS, RHEL, AlmaLinux, Rocky Linux, openSUSE, etc.).
An RPM package (.rpm) is defined by the .rpm extension. It may include the following:
- Binary Files (executables) – computer programs that run on a computer (sh, ssh, sshd, and so on)
- Configuration Files – Different configuration related information of software (sshd.conf, updatedb.conf etc.)
- Document Files – Required documentation written for the packages (README, Version, AUTHOR, etc.)
The name of an RPM package follows this format:
<package-name>-<version>-<release>.<architecture>.rpm
An example:
git-2.8.4-1.ep7.x86_64.rpm
First Step To Create An RPM Package
We will now create an RPM of our own. As we are creating an RPM package, we will need an RHEL based distro, like RedHat or CentOS.
We need to install the rpmdevtools package to build the RPM package. We will also install rpmlint to check for errors of our package files.
Command for installing the packages is below:
yum install -y rpmdevtools rpmlint
After installing rpmdevtools
, our package creation process will begin. We usually create an RPM as a non-root user, not root user. So we first switch to a non-root user.
Now we first have to create the file tree, which is the directory and configuration directory boilerplate we need to build RPM packages.
In my case, I’m in the non-root user’s (called xyz) home directory and I’ll create the new directory.
To do this run:
rpmdev-setuptree
If we check the contents and list subdirectories recursively with $ ls -R
command, we can easily list and see the structure of the rpmbuild
directory.
Here we can see the different subdirectories under this rpmbuild
directory.
Let’s now understand the subdirectories under rpmbuild
. The directories are:
- rpmbuild/BUILD: This directory is deployed during the RPM package’s build process. This is where temporary files are kept and so on.
- rpmbuild/RPMS: This directory contains RPM packages for several architectures and noarch if requested in the .spec file or in the build.
- rpmbuild/SOURCES: This directory contains sources of the package. The sources are usually compressed as .tar.gz or .tgz files.
- rpmbuild/SPEC: This directory contains the .spec files where it is specified how a package is created and related information.
- rpmbuild/SRPMS: In this directory the .src or source RPM packages are located.
Creating the bash script
Now we create a simple bash script which will echo something on the terminal. We will write a .sh file and edit it now using nano.
Now we write the script and save it. This is just a basic bash script, which will echo Hello World and some other texts in the terminal. To start any .sh or bash script, we write #!/bin/bash
at the first line.
We can also run it to check whether it shows the right output or not.
To run a script, we use the sh
command. Given below:
sh myrpm.sh
Now we create a new folder for our package, named myrpm-0.0.1
with 0.0.1
denoting the version of the package we will create. To do this run:
mkdir myrpm-0.0.1
Then we move the .sh
script in this directory. To do this run:
mv myrpm.sh myrpm-0.0.1
Now let us create an archived or zipped folder of it. For this we will use tar and name or tarball as myrpm-0.0.1.tar.gz. To do this run:
tar -cvzf myrpm-0.0.1.tar.gz myrpm-0.0.1
Now we move it to the Sources directory and list the contents of the directory via following commands.
mv myrpm-0.0.1.tar.gz rpmbuild/SOURCES ls -R rpmbuild/
.spec file creation
An RPM package is defined and documented by a .spec file.
The command to create a .spec file named as myrpm is:
rpmdev-newspec myrpm
Now we move the spec file to /SPECS directory. And then we can check our rpmbuild directory hierarchy how it looks like using tree command.
tree rpmbuild
Let us now open the spec file using nano
(or your preferred text editor) and check what it contains.
Our generated .spec file contains a basic boilerplate of some pre-ready information regarding a RPM package.
Let us first understand in brief what a .spec file contains.
Preamble Items:
- Name: Name is what the package will be called. It should match the name of the .spec file.
- Version: The package version.
- Release: The package version number.
- Summary: Simple summary of what the package is about.
- License: The license of the software or package used.
- Source0: Path or URL to the compressed archive file of the source of RPM.
- BuildArch: For which architecture the package is built for.
- BuildRequires: List of packages or software needed to build the RPM.
- Requires: List of packages or software needed to run the RPM after installing.
Body Items:
%description
– A simple description of the software packaged in the RPM.%prep
– This section contains the script that will first execute during the build process of the RPM.%build
– This section is for performing the build.%install
– This section is also executed like%prep
and%build
section and is used for the installation.%files
– This section contains the list of files that the package contains.%clean
– This section instructs the RPM to clean up any files that are not needed after build.%changelog
– A log of changes may happen to the package between different versions and releases.
We will add information in the .spec file as per our own. We will now add in the Name section and Version section, but Name must match the .spec file name.
For Release, here 1%{?dist}
means if %{dist}
is defined, insert its value here. If not, do nothing.
This dist tag is known as distro or distribution tag. It means which distro is used for this RPM package creation, like if we build an RPM on CentOS 7 we should get it as .el7
.
We also add in the Summary section and set the Source0 section as per our tarball in /SOURCES
directory. We removed the “BuildArch” and “BuildRequires” sections as we don’t need them for now. We set the “Requires” section as bash because we are ruuning a .sh script for which bash is needed.
After that we added a description and set the %prep
section as with another shortcut %setup
with -q
. We can also leave it as %autosetup
.
As of now you can see that there are a lot of shortcuts or we can say predefined variables starting with %
in .spec files. They’re called macros. RPM provides predefined a lot of macros to make package maintenance simpler. You can get all the information regarding macros in the Fedora official documentation.
https://docs.fedoraproject.org/en-US/packaging-guidelines/RPMMacros/
For now, the shortcut’s full evaluated form can be found if we run the commands in the terminal. The syntax is:
rpm --eval <macro>
For example, if I want to see what the %{_bindir}
contains, we run the command:
rpm --eval '%{_bindir}'
Now we change the section with macros on our own and add something on these.
$RPM_BUILD_ROOT
or the equivalent %{buildroot}
macro holds the directory under which RPM will look for files to package.
In the %install
section we first clear the previous directories, then create a new directory with the %{_bindir}
macro and finally copy the .sh script to this directory.
Then we clean the directory in the %clean
section. In the %file
section, we added the .sh script.
And in the %changelog
section we’ve just added the release version of the rpm as log.
Now our spec is ready. We can check this for any error using rpmlint. The command is:
rpmlint rpmbuild/SPECS/myrpm.spec
We get no error, but two warnings. We can ignore the warnings for now.
One is for build section and another one is for invalid url as we haven’t added any build section.
Package creation
There are two types of RPM packages:
- Source RPM (SRPM) – contains source code and a SPEC file
- Binary RPM – contains the binaries built from the sources
We first create the source RPM. Command is:
rpmbuild -bs rpmbuild/SPECS/myrpm.spec
Here -b
is for build , and -s
is for source.
See the man page of rpmbuild
for more clarification.
We can create the binary RPM using below command:
rpmbuild -bb rpmbuild/SPECS/myrpm.spec
Here -b
is for build , and -b
is for binary.
We can also create both source and binary together using the command:
rpmbuild -ba rpmbuild/SPECS/myrpm.spec
Here -b
is for build , and -a
is for all.
Now our rpm package is ready. Let us again check our rpmbuild directory tree and see what it contains now.
Run the command:
tree rpmbuild
Installing the RPM package
To install we can use yum or rpm. Commands are given below:
yum install rpmbuild/RPMS/x86_64/myrpm-0.0.1-1.el8.x86_64.rpm rpm -ivh rpmbuild/RPMS/x86_64/myrpm-0.0.1-1.el8.x86_64.rpm
Here I have switched to root and using yum
I’ve installed my package.
Our package is installed. To check we can use
rpm -qa myrpm
Here -q
is for query, -a
is for all.
We can also use other options with the rpm command.
rpm -qi myrpm
Here -q
is for query, -i
is for info.
We can also check where our script is listed and located using the following command:
rpm -ql myrpm
Here -q
is for query, -l
is for list.
Removing the RPM package
We can remove packages using yum remove or rpm. The commands are:
yum remove myrpm rpm -Ve myrpm
Here -V
is for verbose, and -e
is for erase.
Conclusion
That’s it. This is the minimalistic process of creating an rpm package of our own, installing it, and finally removing it. If you have any feedback or questions feel free to leave a comment.