3 Building Non-Modular Source RPM Packages

This chapter describes how to build non-modular source RPM packages. Two methods are provided: the first is more direct and uses the rpmbuild utility directly; the second requires that you set up the mock utility and configure the system to process builds using this tool. There are several advantages to using the mock utility to build packages and you should consider using this tool if you intend to do mutliple or regular builds of different packages.

Using rpmbuild Directly

The simplest approach for building is to use the rpmbuild --rebuild command to build a binary RPM package directly from the source RPM package. This tool takes the source RPM package and extracts it into a standard hierarchy within ~/rpmbuild/; the tool checks that all of the build dependencies specified in the associated SPEC file that is stored in ~/rpmbuild/SPECS are satisfied; the source tarballs in ~/rpmbuild/SOURCES are extracted into a build directory, and are each built before being packaged into a binary RPM package, which is stored in ~/rpmbuild/RPMS.

Important:

Do not build source RPM packages as the root user. The build processes often run scripts and processes that you may not have full control over and that could easily cause system failure or could compromise a system.

Note:

When using the rpmbuild tool to build source RPM packages directly, you may see warning messages similar to the following:

warning: user mockbuild does not exist - using root

These warnings can be ignored, as they are related to the way these source packages are set up for build by using the mock utility within a more complex build environment.

If the build dependencies that are specified in the SPEC file are not satisfied, the rpmbuild --rebuild command errors out and lists the failed dependencies, for example:

rpmbuild --rebuild bash-4.4.19-12.el8.src.rpm
Installing bash-4.4.19-12.el8.src.rpm
...
error: Failed build dependencies:
  autoconf is needed by bash-4.4.19-12.el8.x86_64
  ncurses-devel is needed by bash-4.4.19-12.el8.x86_64
  texinfo is needed by bash-4.4.19-12.el8.x86_64

You can either manually install each dependency that is listed in the warning; or, you can use the dnf builddep command to resolve all of the build dependencies specified in the SPEC file that was extracted when you ran the rpmbuildcommand, for example:

sudo dnf builddep -y ~/rpmbuild/SPECS/bash.spec

If all of the build dependencies are satisfied and the source package has been created appropriately, the rpmbuild --rebuild command completes after the binary package build is complete. You can access the package in ~/rpmbuild/RPMS.

If you need to modify source, apply alternative patches, or edit the SPEC file to perform alternate actions during different stages of the build process, you can do so and then build the binary and source packages again directly from the SPEC file, for example:

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

Using the mock Utility to Build Sources

The mock utility provides a wrapper that can help build sources safely and can handle build dependency resolution for you in the background. This tool sets up a chroot directory to handle the build process. By creating a chroot environment, several potential build issues are handled automatically. Most importantly, the build process itself is kept safe from affecting the host system. The chroot environment can install build dependency packages and everything that is required to complete the build, without actually installing unwanted packages into the host system so that the host system can remain relatively clean of build artifacts. Because the chroot environment contains the build process and protects the host system, build processes are effectively privileged within the contained environment, which helps facilitate a straightforward build and minimize potential errors. Finally, build dependencies can be automatically resolved by using the mock utility, which can safely and automatically trigger the appropriate dnf builddep command, as required.

The mock utility requires some preliminary configuration before using it to build for Oracle Linux sources. A useful starting point is to create a /etc/mock/templates/ol-8.tpl template file with a configuration similar to the following:

config_opts['chroot_setup_cmd'] = 'install tar gcc-c++ redhat-rpm-config oraclelinux-release which xz sed \
                                   make bzip2 gzip gcc coreutils unzip shadow-utils diffutils cpio bash gawk \
                                   rpm-build info patch util-linux findutils grep'
config_opts['dist'] = 'el8'  # only useful for --resultdir variable subst
config_opts['extra_chroot_dirs'] = [ '/run/lock', ]
config_opts['releasever'] = '8'
config_opts['package_manager'] = 'dnf'
config_opts['root'] = 'ol-8-{{ target_arch }}'

config_opts['dnf.conf'] = """
[main]
keepcache=1
debuglevel=2
reposdir=/dev/null
logfile=/var/log/yum.log
retries=20
obsoletes=1
gpgcheck=1
assumeyes=1
syslog_ident=mock
syslog_device=
install_weak_deps=0
metadata_expire=0
best=1
module_platform_id=platform:el8
protected_packages=

# repos
[ol8_baseos_latest]
name=Oracle Linux 8 BaseOS Latest ($basearch)
baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/$basearch/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
gpgcheck=1
enabled=1

[ol8_appstream]
name=Oracle Linux 8 Application Stream ($basearch)
baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/appstream/$basearch/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
gpgcheck=1
enabled=1

[ol8_codeready_builder]
name=Oracle Linux 8 CodeReady Builder ($basearch) - Unsupported
baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/codeready/builder/$basearch/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
gpgcheck=1
enabled=1

[ol8_distro_builder]
name=Oracle Linux 8 Distro Builder ($basearch) - Unsupported
baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/distro/builder/$basearch/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
gpgcheck=1
enabled=1

[ol8_developer_EPEL]
name=Oracle Linux $releasever EPEL Packages for Development ($basearch) 
baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/developer/EPEL/$basearch/ 
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle 
gpgcheck=1 
enabled=1

"""

Replace /etc/mock/default.cfg with the following content, substituting x86_64 with aarch64 if you intend to build packages for Arm platforms:

include('templates/ol-8.tpl')
config_opts['target_arch'] = 'x86_64'
config_opts['legal_host_arches'] = ('x86_64',)

Before any user can use the mock utility, the user must be added to the mock group. You can do this by running the following command:

sudo /usr/sbin/usermod -a -G mock $USER

Note that if the user is currently logged in, the user may need to log out and then log back in to the system for the change in group permissions to become active.

When the configuration is in place, you can start to build source packages by using the mock utility. For example, to simply build a package directly from a source RPM package:

mock --rebuild bash-4.4.19-12.el8.src.rpm

Packages are built in a chroot environment that is created for the build root that is defined in the template. You can access the file system used in the chroot in /var/lib/mock. For example, using the configurations presented in this documentation would be similar to the following:

ls /var/lib/mock/ol-8-x86_64/root/builddir/build/SRPMS/
bash-4.4.19-12.el8.src.rpm
ls -lh /var/lib/mock/ol-8-x86_64/root/builddir/build/RPMS/
total 4.9M
-rw-r--r--. 1 root mock 1.6M Feb 3 02:10 bash-4.4.19-12.el8.x86_64.rpm
-rw-r--r--. 1 root mock 1.2M Feb 3 02:10 bash-debuginfo-4.4.19-12.el8.x86_64.rpm
-rw-r--r--. 1 root mock 841K Feb 3 02:10 bash-debugsource-4.4.19-12.el8.x86_64.rpm
-rw-r--r--. 1 root mock 113K Feb 3 02:10 bash-devel-4.4.19-12.el8.x86_64.rpm
-rw-r--r--. 1 root mock 1.3M Feb 3 02:10 bash-doc-4.4.19-12.el8.x86_64.rpm

If you need to work with build artifacts within the mock chroot, you can also do the following:

mock --shell
INFO: mock.py version 2.4 starting (python version = 3.6.8)...
Start(bootstrap): init plugins
INFO: selinux enabled
Finish(bootstrap): init plugins
Start: init plugins
INFO: selinux enabled
Finish: init plugins
INFO: Signal handler active
Start: run
Start(bootstrap): chroot init
INFO: calling preinit hooks
INFO: enabled root cache
INFO: enabled package manager cache
Start(bootstrap): cleaning package manager metadata
Finish(bootstrap): cleaning package manager metadata
INFO: enabled HW Info plugin
Finish(bootstrap): chroot init
Start: chroot init
INFO: calling preinit hooks
INFO: enabled root cache
INFO: enabled package manager cache
Start: cleaning package manager metadata
Finish: cleaning package manager metadata
INFO: enabled HW Info plugin
Finish: chroot init
Start: shell
ls -lah /builddir/
total 20K
drwx------. 1 root 1000 120 Feb  4 09:21 .
dr-xr-xr-x. 1 root root 212 Feb  3 02:06 ..
-rw-------. 1 root root  50 Feb  4 09:21 .bash_history
-rw-r--r--. 1 root 1000  18 Aug  2  2020 .bash_logout
-rw-r--r--. 1 root 1000 141 Aug  2  2020 .bash_profile
-rw-r--r--. 1 root 1000 376 Aug  2  2020 .bashrc
-rw-rw-r--. 1 root root 130 Feb  3 02:06 .rpmmacros
drwxrwxr-x. 1 root 1000  88 Feb  3 02:06 build

You can find out more about mock in the MOCK(1) manual page or at https://github.com/rpm-software-management/mock/.