Chapter 2. Preparing software for RPM packaging
This section explains how to prepare software for RPM packaging. To do so, knowing how to code is not necessary. However, you need to understand the basic concepts, such as What source code is and How programs are made.
2.1. What source code is Copy linkLink copied to clipboard!
This part explains what source code is and shows example source codes of a program written in three different programming languages.
Source code is human-readable instructions to the computer, which describe how to perform a computation. Source code is expressed using a programming language.
2.1.1. Source code examples Copy linkLink copied to clipboard!
This document features three versions of the Hello World program written in three different programming languages:
Each version is packaged differently.
These versions of the Hello World program cover the three major use cases of an RPM packager.
2.1.1.1. Hello World written in bash Copy linkLink copied to clipboard!
The bello project implements Hello World in bash. The implementation only contains the bello shell script. The purpose of the program is to output Hello World on the command line.
The bello file has the following syntax:
#!/bin/bash printf "Hello World\n"
#!/bin/bash
printf "Hello World\n"
2.1.1.2. Hello World written in Python Copy linkLink copied to clipboard!
The pello project implements Hello World in Python. The implementation only contains the pello.py program. The purpose of the program is to output Hello World on the command line.
The pello.py file has the following syntax:
#!/usr/bin/python3
print("Hello World")
#!/usr/bin/python3
print("Hello World")
2.1.1.3. Hello World written in C Copy linkLink copied to clipboard!
The cello project implements Hello World in C. The implementation only contains the cello.c and the Makefile files, so the resulting tar.gz archive will have two files apart from the LICENSE file.
The purpose of the program is to output Hello World on the command line.
The cello.c file has the following syntax:
2.2. How programs are made Copy linkLink copied to clipboard!
Methods of conversion from human-readable source code to machine code (instructions that the computer follows to execute the program) include the following:
- The program is natively compiled.
- The program is interpreted by raw interpreting.
- The program is interpreted by byte compiling.
2.2.1. Natively Compiled Code Copy linkLink copied to clipboard!
Natively compiled software is software written in a programming language that compiles to machine code with a resulting binary executable file. Such software can be run stand-alone.
RPM packages built this way are architecture-specific.
If you compile such software on a computer that uses a 64-bit (x86_64) AMD or Intel processor, it does not execute on a 32-bit (x86) AMD or Intel processor. The resulting package has architecture specified in its name.
2.2.2. Interpreted Code Copy linkLink copied to clipboard!
Some programming languages, such as bash or Python, do not compile to machine code. Instead, their programs' source code is executed step by step, without prior transformations, by a Language Interpreter or a Language Virtual Machine.
Software written entirely in interpreted programming languages is not architecture-specific. Hence, the resulting RPM Package has the noarch string in its name.
Interpreted languages are either Raw-interpreted programs or Byte-compiled programs. These two types differ in program build process and in packaging procedure.
2.2.2.1. Raw-interpreted programs Copy linkLink copied to clipboard!
Raw-interpreted language programs do not need to be compiled and are directly executed by the interpreter.
2.2.2.2. Byte-compiled programs Copy linkLink copied to clipboard!
Byte-compiled languages need to be compiled into byte code, which is then executed by the language virtual machine.
Some languages offer a choice: they can be raw-interpreted or byte-compiled.
2.3. Building software from source Copy linkLink copied to clipboard!
This part describes how to build software from source code.
For software written in compiled languages, the source code goes through a build process, producing machine code. This process, commonly called compiling or translating, varies for different languages. The resulting built software can be run, which makes the computer perform the task specified by the programmer.
For software written in raw interpreted languages, the source code is not built, but executed directly.
For software written in byte-compiled interpreted languages, the source code is compiled into byte code, which is then executed by the language virtual machine.
2.3.1. Natively Compiled Code Copy linkLink copied to clipboard!
This section shows how to build the cello.c program written in the C language into an executable.
cello.c
2.3.1.1. Manual building Copy linkLink copied to clipboard!
If you want to build the cello.c program manually, use this procedure:
Procedure
Invoke the C compiler from the GNU Compiler Collection to compile the source code into binary:
gcc -g -o cello cello.c
gcc -g -o cello cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the resulting output binary
cello:./cello
$ ./cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3.1.2. Automated building Copy linkLink copied to clipboard!
Large-scale software commonly uses automated building that is done by creating the Makefile file and then running the GNU make utility.
If you want to use the automated building to build the cello.c program, use this procedure:
Procedure
To set up automated building, create the
Makefilefile with the following content in the same directory ascello.c.Makefilecello: gcc -g -o cello cello.c clean: rm cello
cello: gcc -g -o cello cello.c clean: rm celloCopy to Clipboard Copied! Toggle word wrap Toggle overflow Note that the lines under
cello:andclean:must begin with a tab space.To build the software, run the
makecommand:make
$ make make: 'cello' is up to date.Copy to Clipboard Copied! Toggle word wrap Toggle overflow Since there is already a build available, run the
make cleancommand, and after run themakecommand again:make clean make
$ make clean rm cello $ make gcc -g -o cello cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteTrying to build the program after another build has no effect.
make
$ make make: 'cello' is up to date.Copy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the program:
./cello
$ ./cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
You have now compiled a program both manually and using a build tool.
2.3.2. Interpreting code Copy linkLink copied to clipboard!
This section shows how to byte-compile a program written in Python and raw-interpret a program written in bash.
In the two examples below, the #! line at the top of the file is known as a shebang, and is not part of the programming language source code.
The shebang enables using a text file as an executable: the system program loader parses the line containing the shebang to get a path to the binary executable, which is then used as the programming language interpreter. The functionality requires the text file to be marked as executable.
2.3.2.1. Byte-compiling code Copy linkLink copied to clipboard!
This section shows how to compile the pello.py program written in Python into byte code, which is then executed by the Python language virtual machine.
Python source code can also be raw-interpreted, but the byte-compiled version is faster. Hence, RPM Packagers prefer to package the byte-compiled version for distribution to end users.
pello.py
#!/usr/bin/python3
print("Hello World")
#!/usr/bin/python3
print("Hello World")
Procedure for byte-compiling programs varies depending on the following factors:
- Programming language
- Language’s virtual machine
- Tools and processes used with that language
Python is often byte-compiled, but not in the way described here. The following procedure aims not to conform to the community standards, but to be simple. For real-world Python guidelines, see Software Packaging and Distribution.
Use this procedure to compile pello.py into byte code:
Procedure
Byte-compile the
pello.pyfile:python -m compileall pello.py file pello.pyc
$ python -m compileall pello.py $ file pello.pyc pello.pyc: python 2.7 byte-compiledCopy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the byte code in
pello.pyc:python pello.pyc
$ python pello.pyc Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3.2.2. Raw-interpreting code Copy linkLink copied to clipboard!
This section shows how to raw-interpret the bello program written in the bash shell built-in language.
bello
#!/bin/bash printf "Hello World\n"
#!/bin/bash
printf "Hello World\n"
Programs written in shell scripting languages, like bash, are raw-interpreted.
Procedure
Make the file with source code executable and run it:
chmod +x bello ./bello
$ chmod +x bello $ ./bello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.4. Patching software Copy linkLink copied to clipboard!
This section explains how to patch the software.
In RPM packaging, instead of modifying the original source code, we keep it, and use patches on it.
A patch is a source code that updates other source code. It is formatted as a diff, because it represents what is different between two versions of the text. A diff is created using the diff utility, which is then applied to the source code using the patch utility.
Software developers often use Version Control Systems such as git to manage their code base. Such tools provide their own methods of creating diffs or patching software.
The following example shows how to create a patch from the original source code using diff, and how to apply the patch using patch. Patching is used in a later section when creating an RPM; see Section 3.2, “Working with SPEC files”.
This procedure shows how to create a patch from the original source code for cello.c.
Procedure
Preserve the original source code:
cp -p cello.c cello.c.orig
$ cp -p cello.c cello.c.origCopy to Clipboard Copied! Toggle word wrap Toggle overflow The
-poption is used to preserve mode, ownership, and timestamps.Modify
cello.cas needed:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Generate a patch using the
diffutility:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Lines starting with a
-are removed from the original source code and replaced with the lines that start with+.Using the
Nauroptions with thediffcommand is recommended because it fits the majority of usual use cases. However, in this particular case, only the-uoption is necessary. Particular options ensure the following:-
-N(or--new-file) - Handles absent files as if they were empty files. -
-a(or--text) - Treats all files as text. As a result, the files thatdiffclassifies as binaries are not ignored. -
-u(or-U NUMor--unified[=NUM]) - Returns output in the form of output NUM (default 3) lines of unified context. This is an easily readable format that allows fuzzy matching when applying the patch to a changed source tree. -r(or--recursive) - Recursively compares any subdirectories that are found.For more information on common arguments for the
diffutility, see thediffmanual page.
-
Save the patch to a file:
diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
$ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patchCopy to Clipboard Copied! Toggle word wrap Toggle overflow Restore the original
cello.c:cp cello.c.orig cello.c
$ cp cello.c.orig cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow The original
cello.cmust be retained, because when an RPM is built, the original file is used, not the modified one. For more information, see Section 3.2, “Working with SPEC files”.
The following procedure shows how to patch cello.c using cello-output-first-patch.patch, built the patched program, and run it.
Redirect the patch file to the
patchcommand:patch < cello-output-first-patch.patch
$ patch < cello-output-first-patch.patch patching file cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow Check that the contents of
cello.cnow reflect the patch:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Build and run the patched
cello.c:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5. Installing arbitrary artifacts Copy linkLink copied to clipboard!
Unix-like systems use the Filesystem Hierarchy Standard (FHS) to specify a directory suitable for a particular file.
Files installed from the RPM packages are placed according to FHS. For example, an executable file should go into a directory that is in the system $PATH variable.
In the context of this documentation, an Arbitrary Artifact is anything installed from an RPM to the system. For RPM and for the system it can be a script, a binary compiled from the package’s source code, a pre-compiled binary, or any other file.
This section describes two common ways of placing Arbitrary Artifacts in the system:
2.5.1. Using the install command Copy linkLink copied to clipboard!
Packagers often use the install command in cases when build automation tooling such as GNU make is not optimal; for example if the packaged program does not need extra overhead.
The install command is provided to the system by coreutils, which places the artifact to the specified directory in the file system with a specified set of permissions.
The following procedure uses the bello file that was previously created as the arbitrary artifact as a subject to this installation method.
Procedure
Run the
installcommand to place thebellofile into the/usr/bindirectory with permissions common for executable scripts:sudo install -m 0755 bello /usr/bin/bello
$ sudo install -m 0755 bello /usr/bin/belloCopy to Clipboard Copied! Toggle word wrap Toggle overflow As a result,
bellois now located in the directory that is listed in the$PATHvariable.Execute
bellofrom any directory without specifying its full path:cd ~ bello
$ cd ~ $ bello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5.2. Using the make install command Copy linkLink copied to clipboard!
Using the make install command is an automated way to install built software to the system. In this case, you need to specify how to install the arbitrary artifacts to the system in the Makefile that is usually written by the developer.
This procedure shows how to install a build artifact into a chosen location on the system.
Procedure
Add the
installsection to theMakefile:MakefileCopy to Clipboard Copied! Toggle word wrap Toggle overflow Note that the lines under
cello:,clean:, andinstall:must begin with a tab space.NoteThe $(DESTDIR) variable is a GNU make built-in and is commonly used to specify installation to a directory different than the root directory.
Now you can use
Makefilenot only to build software, but also to install it to the target system.Build and install the
cello.cprogram:make sudo make install
$ make gcc -g -o cello cello.c $ sudo make install install -m 0755 cello /usr/bin/celloCopy to Clipboard Copied! Toggle word wrap Toggle overflow As a result,
cellois now located in the directory that is listed in the$PATHvariable.Execute
cellofrom any directory without specifying its full path:cd ~ cello
$ cd ~ $ cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.6. Preparing source code for packaging Copy linkLink copied to clipboard!
Developers often distribute software as compressed archives of source code, which are then used to create packages. RPM packagers work with a ready source code archive.
Software should be distributed with a software license.
This procedure uses the GPLv3 license text as an example content of the LICENSE file.
Procedure
Create a
LICENSEfile, and make sure that it includes the following content:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Additional resources
- The code created in this section can be found here.
2.7. Putting source code into tarball Copy linkLink copied to clipboard!
This section describes how to put each of the three Hello World programs introduced in Section 2.1.1, “Source code examples” into a gzip-compressed tarball, which is a common way to release the software to be later packaged for distribution.
2.7.1. Putting the bello project into tarball Copy linkLink copied to clipboard!
The bello project implements Hello World in bash. The implementation only contains the bello shell script, so the resulting tar.gz archive will have only one file apart from the LICENSE file.
This procedure shows how to prepare the bello project for distribution.
Prerequisites
Considering that this is version 0.1 of the program.
Procedure
Put all required files into a single directory:
mkdir /tmp/bello-0.1 mv ~/bello /tmp/bello-0.1/ cp /tmp/LICENSE /tmp/bello-0.1/
$ mkdir /tmp/bello-0.1 $ mv ~/bello /tmp/bello-0.1/ $ cp /tmp/LICENSE /tmp/bello-0.1/Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in bash, see Section 2.1.1.1, “Hello World written in bash”.
2.7.2. Putting the pello project into tarball Copy linkLink copied to clipboard!
The pello project implements Hello World in Python. The implementation only contains the pello.py program, so the resulting tar.gz archive will have only one file apart from the LICENSE file.
This procedure shows how to prepare the pello project for distribution.
Prerequisites
Considering that this is version 0.1.1 of the program.
Procedure
Put all required files into a single directory:
mkdir /tmp/pello-0.1.2 mv ~/pello.py /tmp/pello-0.1.2/ cp /tmp/LICENSE /tmp/pello-0.1.2/
$ mkdir /tmp/pello-0.1.2 $ mv ~/pello.py /tmp/pello-0.1.2/ $ cp /tmp/LICENSE /tmp/pello-0.1.2/Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in Python, see Section 2.1.1.2, “Hello World written in Python”.
2.7.3. Putting the cello project into tarball Copy linkLink copied to clipboard!
The cello project implements Hello World in C. The implementation only contains the cello.c and the Makefile files, so the resulting tar.gz archive will have two files apart from the LICENSE file.
The patch file is not distributed in the archive with the program. The RPM Packager applies the patch when the RPM is built. The patch will be placed into the ~/rpmbuild/SOURCES/ directory alongside the .tar.gz archive.
This procedure shows how to prepare the cello project for distribution.
Prerequisites
Considering that this is version 1.0 of the program.
Procedure
Put all required files into a single directory:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Add the patch:
mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/
$ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in C, see Section 2.1.1.3, “Hello World written in C”.